BryanLeeNavigate back to the homepage

记一次 new Array() 时错误传递参数导致 TypeScript 报错的来龙去脉

Bryan Lee
May 25th, 2020 · 1 min read

起因

在写业务代码时,leader 打算把 AngularJS 代码中的众多 $provide.value 抽离出来,放到类似 common 等文件夹下并 export,在需要用到的文件中再单独 import 进来。

我负责重构的这个 $provide.value 是一个对象数组,并且需要在 Array 基类的基础上为它添加一个方法。

最终实现代码如下:

1interface IProvider {
2 type: string;
3 name: string;
4}
5
6class Providers extends Array<IProvider> {
7 constructor(data: IProvider[]) {
8 super();
9 Object.assign(this, data);
10 }
11
12 $getProvider(type: string): IProvider | undefined {
13 return this.find((item) => item.type === type);
14 }
15}
16
17export const demo = new Providers(
18 [
19 {
20 type: "temp",
21 name: "Provider1"
22 },
23 {
24 type: "test",
25 name: "Provider2"
26 }
27 ]
28);

重点来说说这个部分的代码:

1constructor(data: IProvider[]) {
2 super();
3 Object.assign(this, data);
4 }

这里纯粹是无奈妥协之举,不写的话,发现 new Providers() 的时候直接传入一整个对象数组的数据,将得到如下报错:

TypeScript 的报错信息

因为不想被 block 住,完成 ticket 是首要的,所以没有深究这个报错,便选择了折中的解决方案。后续空闲下来,再看这个部分的代码,认为 Object.assign(this, data); 这个地方写的异常丑陋, new Providers() 的传值被暴力复制给了 this,完全失去了传参意义,且代码可维护性极差。

过程

开始我怀疑数组定义写错了,是不是应该写成 Array<IProvider[]>,这样 new Providers() 的时候传一个数组进去就没有任何问题,不过这样做的得到的是一个二维数组,与预想的数据结构有所出入。

之后跟 leader 讨论了一下,猜测是不是 TypeScript 对这部分的泛型定义有问题,遂开始查阅 MDN 的文档,当即发现 new Array() 的时候直接传入一整个数组的数据作为参数与我预想的结果确实有所出入:

  • New Array() 所接收的参数说明

  • 比如

    1// 想得到数组 `[{name: demo}, {name: test}]`
    2// 错误,返回的是 [[{name: demo}, {name: test}]]
    3new Array([{name: "demo"}, {name: "test"}])
    4
    5// 正确
    6new Array(...[{name: "demo"}, {name: "test"}])

其实,在 VSCode 中,Command + 光标 点击 new Providers 也能看到该方法的类型定义:

Array constructor 的类型定义

此外,在 MDN/Array() constructor 中也有相同的定义,所以不准确的概括下:如果没有定义 constructor() 所接收的参数类型,那么 new <T>() 会默认接收 arrayLength: number...items: T[] 这两种参数类型。

既然知道了原因那么,问题就迎刃而解了,不啰嗦上代码:

1interface IProvider {
2 type: string;
3 name: string;
4}
5
6class Providers extends Array<IProvider> {
7 $getProvider(type: string): IProvider | undefined {
8 return this.find((item) => item.type === type);
9 }
10}
11
12export const demo = new Providers(
13 ...[
14 {
15 type: "temp",
16 name: "Provider1"
17 },
18 {
19 type: "test",
20 name: "Provider2"
21 }
22 ]
23);

总结

Class 写的太少,TypeScript 用的不熟。

More articles from Bryan Lee

在 macOS 中配置大小写敏感的 APFS 卷存储代码

如果你的 macOS 是刚到手没多久的,那么直接重装,设置文件系统为大小写敏感就好了。但是如果你的 macOS 已经使用过一段时间,做了许多设置且存储了很多重要文件了,再重装就有些划不来

August 14th, 2022 · 1 min read

一次因为错误配置 Git 而导致的「事故」

push.default 的配置选项有五个:nothing, current, upstream, simple,matching。

July 17th, 2021 · 1 min read
© 2020–2022 Bryan Lee
Link to $https://github.com/libyLink to $https://bit.ly/3CSfXii