由于允许这样做,因此在我的打字稿代码库中发现了一个严重的错误:
const nums: number[] = { ...[1, 2, 3] } // should have been [ ...[1,2,3] ]
a.join('-')
// runtime error: a.join is not a function
为什么将数组解构为对象,然后分配给数组,从而导致容易避免的运行时异常?
答案 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,给它一个?,并描述您如何被它咬伤,但是我不知道它的确会做很多。
好的,希望能有所帮助;祝你好运!