如果删除重复的构造函数,通用类表达式就会被破坏

2024-01-12

我偶然发现了这种奇怪的行为

如果我删除其中的构造函数签名之一IFoo编译器触发以下错误:Type 'typeof (Anonymous class)' is not assignable to type 'IFoo'.

到底发生了什么?

type Foo<T> = {
  [S in keyof T]: T[S]
}

interface IFoo {
  new <T>(): Foo<T>;
  new <T>(): Foo<T>; // KO if removed
}

const Foo: IFoo = (class {})

const foo = new Foo();

操场 https://www.typescriptlang.org/play?#code/C4TwDgpgBAYg9nAPAFQHxQLxQN4FgBQUUA2gMpQCWAdlANYQhwBmUyAugFytlsEC%20BAtWAQATkwCGAY2gBJeHBwEiVCAHcoKVAAoAlFwVaA3MqiqNWvQYTGoAejtQA0gHlKLURAC2cAG4QAE35BfCk4KgBnYFgELnkETChtKQAbCQiInD5dELDI6KYErHMYuD0TfCA


TL;DR: 重载 https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads哪个是generic https://www.typescriptlang.org/docs/handbook/2/generics.html没有正确进行类型检查,如中所述微软/TypeScript#26631 https://github.com/microsoft/TypeScript/issues/26631 and 微软/TypeScript#50050 https://github.com/microsoft/TypeScript/issues/50050.


Your IFoo接口声称是采用一个类型参数的泛型类构造函数的类型T 但没有实际值参数,并返回类型的类实例Foo<T>这或多或少与T(因为这是身份映射类型 https://www.typescriptlang.org/docs/handbook/2/mapped-types.html复制所有非签名属性)。所以我有一个价值Foo类型的IFoo,那么我大概可以这样写:

declare const Foo: IFoo;
const withStringA = new Foo<{a: string}>();
withStringA.a.toUpperCase();
// const withStringA: Foo<{ a: string; }>
const withNumberA = new Foo<{a: number}>();
// const withNumberA: Foo<{ a: number; }> 
withNumberA.a.toFixed();

它将作为以下 JavaScript 发出:

const withStringA = new Foo();
withStringA.a.toUpperCase();
const withNumberA = new Foo();
withNumberA.a.toFixed();

但两者withStringA and withNumberA使用完全相同的构造调用进行初始化,new Foo()。怎么能withStringA.a属于类型string but withNumberA.a属于类型number?没有任何合理的机制可以让这种事情发生。没有魔法,IFoo is 无法实现的,至少不安全。

尤其,class {}没有正确实施IFoo。它根本没有属性,更不用说a神奇的财产string or number取决于运行时不可用的信息。如果您尝试运行它,您将收到运行时错误:

const Foo: IFoo = class { };
const withStringA = new Foo<{ a: string }>();
// const withStringA: Foo<{ a: string; }>
withStringA.a.toUpperCase(); // RUNTIME ERROR ???? x.a is undefined 

So if IFoo无法安全实施,why编译器没有警告你吗?


事实上,正如您所注意到的,您do当您删除第二个时收到警告构建签名 https://www.typescriptlang.org/docs/handbook/2/functions.html#construct-signatures:

interface IFoo {
  new <T>(): Foo<T>;
}

const Foo: IFoo = class { }; // error!
// -> ~~~
// Type 'typeof Foo' is not assignable to type 'IFoo'.

这个错误是expected and good.

但一旦你添加第二个,超载 https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads构造签名,错误消失:

interface IFoo {
  new <T>(): Foo<T>;
  new <T>(): Foo<T>; 
}

const Foo: IFoo = class { }; // okay?!

Why?


问题是generic https://www.typescriptlang.org/docs/handbook/2/generics.html重载没有正确进行类型检查,如中所述微软/TypeScript#26631 https://github.com/microsoft/TypeScript/issues/26631 and 微软/TypeScript#50050 https://github.com/microsoft/TypeScript/issues/50050。类型参数替换为不安全的any type https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#any,这意味着编译器检查分配class {} as if IFoo were:

interface IFooLike {
  new(): Foo<any>;
  new(): Foo<any>;
}

这与以下相同

interface IFooLike {
  new(): any;
  new(): any;
}

确实,class {}确实匹配该类型:

const Foo: IFooLike = class { };

所以不存在编译器错误,尽管应该有。

这实际上是 TypeScript 的设计限制;这是一个错误,但如果他们修复它,许多当前有效(并且恰好是安全的)的代码可能会开始发出编译器警告(因为编译器无法很好地验证重载安全性)。

因此,如果您确实决定对某个类型使用多个泛型调用或构造签名,请小心实现它的方式,因为编译器可能会默默地无法捕获错误。


Playground 代码链接 https://www.typescriptlang.org/play?#code/LAKAdghgtgpgzgBwgYxgAgPIDcA2ATNAb1DTQBcBPBdAMQHs6AeAFQD40BeIk0tAbQDKaAJZg0AaxgU6AMzTMAugC55ghTwC+oHqLIwATjJToAkvTrcQvNGBgB3NC1YAKAJQrzTgNw9Sth05uHgzeaAD0YWgA0hgicvowUHRYMHia2lbk+hSW1sh0YHBkaOYqZgycaMg4EHBwRGgaPpmk+YXFdsJkABYCZPqiAOYAgpX+JSGEaBAqRQNgg40urs3WEVUFRWidPX3zI8FMUzNoc0NeS77bXb39Q8MAdBAPZHQAqgjU+gDCtTBuF3WACU3gA5ZgmACyAFE0NCgUCMEC0IBeDcApTtoAAeTxE9QArmA8DAZKJUmgrm0tjtuqC8VAAEYGUZccaeY4qMB0xn6JYAq7rSkdG60hlMw6MdmnO4LC4aVhXaki7mPZ6vGjCTGpPmZDRVCBkZDdNDOGCuXK8Sl0HAwB44OiDE2udIgLQgDKgSCwRDGNCgujYAx2iAEYiZSjUCZMNiVUPWQQiMSSaRyRQqZhqZ06MB6Qw+8oWWO8caBdyR7yZsPZc2tTbFUpofOVaq1epTJrhSIGfR0fQAQn5kQAtOwAH5jgfyKjoADk4Zgskj09xNjoxRbwkGkHp1vIFjnaGn+enDwyeVr1120pGY3sZclZwWvJWE8FF9u+2G4vvV9l8pab72e4nhed5PgMX44H+FYOzQEFwShWF4URZF0SxHFhHxQliVJAgKXPRUuSZG8HDZaYOUInk5W1NZIlfAjRX0T87zIqV9l-BVhQolUQPVTU8Goxo9QNI1HWrDZCitG07QdU1nVdDJPXgJBUDQYY4BMORC33Txoy4QtSHjUQJCkBdU1UAR1B1U8ExzIwVMbfSbHsIIywgMAKFYVYi2c0tPDcjyvPkzJX3rRsuGbOoGiaUAgo9aAlJ9NSNIAJksLNbLzcwxP8Fz-K8vwfJUPLnRrdpIzKLLwpqSK22aIKgA

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如果删除重复的构造函数,通用类表达式就会被破坏 的相关文章

随机推荐