【问题标题】:Typescript function taking one or array of objectsTypescript 函数采用一个或一组对象
【发布时间】:2016-06-15 11:02:36
【问题描述】:

我们经常使用简单的函数声明,其中函数接受单个对象或某种类型的对象数组。

简单的声明是:

interface ISomeInterface {
    name: string;
}

class SomeClass {
    public names: ISomeInterface[] = [];

    public addNames(names: ISomeInterface | ISomeInterface[]): void {
        names = (!Array.isArray(names)) ? [names] : names;
        this.names = this.names.concat(names);
    }    
}

但是 TypeScript 会抛出“类型不可分配”错误。

有更好的方法吗?显然我们可以有两个独立的函数,但我认为以这种方式处理单个函数和多个函数是相当不错的。

【问题讨论】:

  • Protip:使用 names = [].concat(names) 代替 isArray。在 Chrome 上速度提高 43%。

标签: javascript typescript


【解决方案1】:

打字稿处理这个问题的官方方式是使用多个函数签名,例如:

addNames(names: ISomeInterface): void;
addNames(names: ISomeInterface[]): void;
addNames(names: any): void {
    ...
}

您可以在official handbook here查看更多信息

【讨论】:

  • 是的,我就是这么想的,但是如果你调用addNames("Hello World")它不会抛出任何错误,所以基本上你可以使用:any声明,它会是一样的......
  • 我认为你只需要自己验证它。我不确定它是如何工作的,但您可以尝试将names: any 替换为name?: ISomeInterface, names?: ISomeInterface[]。这应该可以防止传递一个字符串,但它可以让你什么也不传递,或者两者都传递。然后在函数内部使用names || [name],它会非常无缝。
【解决方案2】:

你也可以使用 rest 参数:

interface ISomeInterface {
    name: string;
}

class SomeClass {
    public names: ISomeInterface[] = []; // create an instance if applicable.

    addNames(...names: ISomeInterface[]): void {
        // the names argument will always be an array
        this.names = this.names.concat(names);
    }
}

你可以这样称呼它:

addNames(name1); // just pass one
addNames(name1, name2, name3); // pass more comma separated
addNames(...[name1, name2, name3]); // pass an array.

请注意,我删除了 function 关键字,否则主体块内的 this 关键字可能会失去作用域,具体取决于调用它的人。

【讨论】:

  • 是的,漂亮,我确实知道rest param,但我不知道的是它会将自己编译成params,始终是一个数组。谢谢!
  • 我也不知道编译下来的其余参数。这是一个巧妙的解决方案!
  • @Tom 我还建议实例化数组,因为它会阻止您在任何地方进行null 检查。 public names: ISomeInterface[] = [];
  • 赞成,这是 IMO 将“一个或一个列表”传递给函数的最佳解决方案。使用联合类型会使 IMO 变得不必要的麻烦。
  • @user2010955 你可以做类似addNames(name: ISomeInterface, ...names: ISomeInterface[])
【解决方案3】:

我想这就是你想要的

interface ISomeInterface {
    name: string;
}

class SomeClass {
    public names: ISomeInterface[];

    addNames(names: ISomeInterface | ISomeInterface[]): void {
        names = (names instanceof Array) ? names : [names];
        this.names = this.names.concat(<ISomeInterface[]>names)
    }    
}

您想使用instanceOf,而不是 isArray。

【讨论】:

  • 对不起,不是我要求的,你的代码仍然给出同样的错误
【解决方案4】:

你可以让它更容易

 addNames(names: ISomeInterface | ISomeInterface[]): void {
        this.names = this.names.concat(names);
 } 

来自MDN

concat() 方法返回一个新数组,该数组由调用它的数组与作为参数提供的数组和/或 连接而成。

【讨论】:

  • 很好的例子说明我是如何过度设计某些东西而不是正确阅读规范......谢谢!我会保留另一个答案,因为它有一些人们似乎不知道的关于 TS 的额外信息,并且它为您提供了更多关于如何传递参数的灵活性
  • 其实这样编译还是报错:Error:(26, 46) TS2345: Argument of type 'ISomeInterface | ISomeInterface[]' is not assignable to parameter of type 'ISomeInterface'. Type 'ISomeInterface[]' is not assignable to type 'ISomeInterface'.
  • @Tom 我已将 tsc 更新为 Version 1.9.0-dev.20160302 现在它对我有用
  • 酷,我还有 1.8.7 但我会相信你 :) 实际上我会接受你的回答我认为考虑到我在另一个答案中提到的... 更好
  • 我有同样的问题,后来我发现了执行此操作的不同语法 addNames(names: ISomeInterface | Array) 只是说这也有效..
猜你喜欢
  • 2022-01-24
  • 2019-05-01
  • 2020-07-15
  • 2019-10-03
  • 2019-02-15
  • 2021-05-14
  • 1970-01-01
  • 1970-01-01
  • 2020-11-28
相关资源
最近更新 更多