为什么允许这样做? const nums:number [] = {... [1、2、3]}

时间:2020-05-11 23:57:30

标签: typescript

由于允许这样做,因此在我的打字稿代码库中发现了一个严重的错误:

const nums: number[] = { ...[1, 2, 3] } // should have been [ ...[1,2,3] ]
a.join('-')
// runtime error: a.join is not a function 

Playground

为什么将数组解构为对象,然后分配给数组,从而导致容易避免的运行时异常?

1 个答案:

答案 0 :(得分:2)

这是TypeScript的设计限制;参见microsoft/TypeScript#34780

类型系统无法将List<string> production = new List<string>(); foreach (var item in data) { var equalsIndex = item.IndexOf("="); if (equalsIndex > 0 && item.Substring(equalsIndex).Contains("production")) { production.Add(item.Substring(0, equalsIndex).Trim()); } } 成员标记为“拥有”或可枚举的成员,因此编译器假定所有成员都是通过传播运算符复制的。作为一种启发式方法,通常就足够了,但是它对原型上设置的任何成员都做错了事,例如类的方法:

interface

如果直接编写interface Whoops { foo(): void; a: number; b: string; } class Oops implements Whoops { foo() { } a = 1; b = ""; } const oopsie = (w: Whoops) => ({ ...w }); oopsie(new Oops()).foo(); // no compiler error // runtime error: oopsie(...).foo is not a function! 声明,则编译器将假定方法声明不可扩展:

class

但不幸的是,type declarations for Array<T> are for an interface而不是declared class。因此,当您将数组扩展到对象中时,编译器会认为所有declare class Whoops { foo(): void; a: number; b: string; } const oopsie = (w: Whoops) => ({ ...w }); oopsie(new Whoops()).foo(); // compiler time error as expected // foo does not exist on {a: number; b: string}; 属性和方法都已复制,因此生成的对象符合Array接口,因此具有{{1} } 方法。糟糕!


Maaaaybe可以更改标准库,以便我们只有Array而不是join()interface Array<T>interface ArrayConstructor,然后declare var Array: ArrayConstructor被认为是可传播的,但我不确定。当我在自己的系统上本地尝试时,它似乎可以正常工作,但是我无法轻松地在Playground或其他在线IDE中重现它,并且不喜欢像declare class Array<T>这样的内置类型无论如何。

或者可以更改语言,以便可以在join()上标记非自有或不可枚举的属性,但我不会指望它(请参阅microsoft/TypeScript#9726

目前,这是TypeScript的设计限制。如果您对此有强烈的兴趣,可以转到microsoft / TypeScript#34780,给它一个?,并描述您如何被它咬伤,但是我不知道它的确会做很多。

好的,希望能有所帮助;祝你好运!

Playground link to code