在JavaScript中使用命名参数(基于TypeScript)

58
我在使用TypeScript的命名参数时遇到了问题。我知道在我使用的方式上不支持它。
但是我该怎么做呢?
TypeScript:
SomeFunction(name1: boolean, 
             name2: boolean, 
             name3: boolean, 
             name4: boolean) // Will occur only one time, 
                            // so the change should be in TypeScript

JavaScript:

$(function () {
       ...SomeFunction({name1:false, 
                        name2:false, 
                        name3:false, 
                        name4:true}); // Will occur 100 times
});

我正在研究(这没有成功):

在JavaScript中是否有一种方法可以在函数调用中提供命名参数?

如何向TypeScript函数参数添加可选的命名参数?

在TypeScript中,我可以做些什么来使用JavaScript中的命名参数?

我想知道的是,当我在TypeScript中使用命名参数的方式时,Visual Studio 2015没有显示语法错误...

附注:我使用的是TypeScript 2.1


4
好的,调用时命名参数在TS和JS中都不存在。为什么你链接到的其他解决方案对你不起作用?它们应该可以。 - deceze
@deceze https://dev59.com/p2gt5IYBdhLWcg3w0wok Ray Perea的帖子(实际上是一个JS对象) - user7425470
那对你来说,这个方法不起作用吗...? - deceze
进入TypeScript后,对象看起来会很奇怪,请看一下http://pasteboard.co/vNZ5OEg16.png(类似的例子) - user7425470
1
传递对象并不等同于命名参数。对象可以被修改(即某种引用参数)。参数可以在本地修改,但该更改在返回时不会被看到;它是一个本地参数。 - Emilio Platzer
5个回答

72

在 JavaScript 和 TypeScript 中不存在真正的命名参数,但是你可以使用 解构模拟命名参数

interface Names {
    name1: boolean
    name2: boolean
    name3: boolean
    name4: boolean
}

function myFunction({name1, name2, name3, name4}: Names) {
    // name1, etc. are boolean
}

注意:类型Names实际上是可选的。下面的JavaScript代码(不带类型)在TS中是有效的:


注意:类型Names实际上是可选的。以下JavaScript代码(无需类型)在TS中仍然有效:
function myFunction({name1, name2, name3, name4}) {
    // name1, etc. are of type any
}

最后一部分虽然是正确的,但它并不是真正的Typescript,这才是主要问题,而不是javascript。 - Some Dev
我该如何在函数中访问它们?编译器一直告诉我它们未定义。 - Martin Eckleben

18

要获得接近于“命名参数”的东西,唯一的方法是使用单个对象参数:

type SomeFunctionParams = {
    name1: boolean;
    name2: boolean;
    name3: boolean;
    name4: boolean;
}

SomeFunction(params: SomeFunctionParams) { ... }

然后:

$(function () {
    SomeFunction({
        name1:false,
        name2:false,
        name3:false,
        name4:true
    });
});

7

匿名接口

我想指出,您也可以在参数列表中不使用名称声明类型,如下所示,这样可以避免我们键入interface myfuncArgs

这仍然强制我重新输入每个参数,这是一件麻烦的事情,但我目前不知道如何避免它。

const assert = require('assert')

function myfunc({
    myInt,
    myString,
  }: {
    myInt: number,
    myString?: string,
  }
) {
  return `${myInt} ${myString}`
}

assert.strictEqual(
  myfunc({
    myInt: 1,
    myString: 'abc',
  }),
  '1 abc'
)

assert.strictEqual(
  myfunc({
    myInt: 1,
  }),
  '1 undefined'
)

// Fails as desired since myInt is not optional and was not given.
//assert.strictEqual(
//  myfunc({
//    myString: 'abc',
//  }),
//  'unefined abc'
//)

// Fails as desired since wrong type of myInt.
//assert.strictEqual(
//  myfunc({
//    myInt: '1',
//    myString: 'abc',
//  }),
//  '1 abc'
//)

编译并运行:

npx tsc main.ts
node main.js

测试环境:

  "dependencies": {
    "@types/node": "^16.11.13",
    "typescript": "^4.5.4"
  }

相关文章:

当存在可选参数时,通常需要显式地定义接口

如果任何一个prop是可选的,就像下面的例子一样,以及大多数“选项”params的情况下,您可能希望明确定义类型,如https://dev59.com/31gQ5IYBdhLWcg3w3Hp-#42108988中所述,因为调用者很可能迟早都需要它来逐步构建选项对象,相关:如何在TypeScript中动态分配属性到对象?

例如,考虑以下有效代码:

const assert = require('assert')

interface myfuncOpts {
  myInt: number,
  myString?: string,
}

function myfunc({
  myInt,
  myString,
}: myfuncOpts) {
  return `${myInt} ${myString}`
}

const opts: myfuncOpts = { myInt: 1 }
if (process.argv.length > 2) {
  opts.myString = 'abc'
}

assert.strictEqual(
  myfunc(opts),
  '1 abc'
)

如果我们在内联中定义了myfuncOpts,那么我们就无法进行const opts: myfuncOpts,然后执行opts.myString = 'abc'会失败,因为在:
const opts = { myInt: 1 }
if (process.argv.length > 2) {
  opts.myString = 'abc'
}

opts 的类型是从初始化中推导出来的,其中不包含 myString

我希望 TypeScript 能够实现一种系统,让我们可以说“使用这个函数的某个参数的类型”。然后我们就可以使用内联的参数类型定义,但仍然可以引用类型,在那种情况下,它将是极乐世界。


0

相对于发送值,这是可读性的一种提升,并且它将值放在函数调用中,使其更易读,就像良好的命名参数一样。 它不会遇到任何你只发送未标记的值时所遇到的问题,因此它比那要好。

var includeIcons:boolean, includeChildren:boolean
await storageTank.save((includeIcons=false), (includeChildren=false))

适用于单个开发者项目,但不建议在开发人员无法就更改的函数签名进行沟通的大型项目中使用。因此,这取决于上下文。


-10
一个简单的解决方案是使用有意义的命名本地变量:
var mungo: boolean=true, mary: boolean=true, and: boolean=true, midge: boolean=true;

zapOn(mungo, mary, and, midge);

这只是一个第一步的简单解决方案... 遵循测试驱动开发原则:让测试失败,让它工作 - 测试变绿(这个建议),然后重构(如果需要,把局部变量放在一个对象中并将其传递到函数中)。

如果您在TypeScript中这样定义zapOn函数:zapOn({mungo,mary=false,midge,and}:TVInterface){...}其中TVInterface {mary?:boolean,mungo:boolean,and:boolean,midge:boolean}并且通过tv对象={mary:undefined,mungo:mungo,and:and,midge:midge}即zapOn(tv),在函数参数级别使用解构变量/参数,变量/参数会正确排列...在“mary”参数的情况下,我们还设置了默认值为false。


3
它不是通过名称参数进行调用。如果函数参数顺序发生变化会发生什么!或者用户按照错误的顺序编写,您的答案建议进行清洁编程,但不要通过名称参数调用函数。 - Mohammad Nikravan
如果您只使用未标记的值,那么这不会遭受任何问题,而且更易读。 - toddmo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接