TypeScript在静态实现中推断出更通用的类型
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TypeScript在静态实现中推断出更通用的类型相关的知识,希望对你有一定的参考价值。
我偶然发现了TypeScript中的奇怪情况,代码如下:
function staticImplements<T>() {
return (constructor: T) => {};
}
enum FruitList {
APPLE,
BANANA,
PEAR,
}
interface FruitInterface {
fruit: FruitList;
}
abstract class Fruit implements FruitInterface {
fruit: FruitList;
constructor(fruit: FruitList) {
this.fruit = fruit;
}
}
interface AppleConstructor {
new(fruit: FruitList.APPLE): AppleInterface;
}
interface AppleInterface extends Fruit {
fruit: FruitList.APPLE;
}
class Apple extends Fruit implements AppleInterface {
fruit: FruitList.APPLE;
constructor(fruit: FruitList) {
super(fruit);
}
}
staticImplements<AppleConstructor>()(Apple);
正如你所看到的,Fruit
的构造函数需要fruit
类型的参数FruitList
,子类Apple
的构造函数也是如此,但fruit
的字段AppleInterface
只需要枚举APPLE
的值FruitList
而不是enum所持有的所有可能值它的父母FruitInterface
。 AppleConstructor
也是如此,它期望参数fruit
是FruitList.APPLE
类型,用于检查Apple
static是否在最后一行实现了与staticImplements
函数的接口。问题是,TypeScript声明它确实没有,但这怎么可能呢?
您的基本问题是TypeScript类型系统有些不健全(因此您可以编写一些非类型安全的代码)。 Soundness是TypeScript的not a goal,虽然如果这个错误很常见,如果你打开一个issue in GitHub,他们可能会有兴趣解决它。我找不到你的确切问题。
这里特别不健康与不执行type variance有关。简而言之,子类型的属性读取可以是协变的(子类可以缩小它们的只读属性),但属性写入只能是逆变的(子类应该扩展它们的只写属性)。如果属性既可以读写,又必须是不变的。
TypeScript允许子类属性是协变的。这意味着当你阅读属性时,事情通常会很好地工作,但是当你编写属性时有时会发生坏事。
让我用更少的代码重申这里的主要问题:
interface A {
x: string | number
}
interface B extends A {
x: number
}
const b: B = {x: 0};
const a: A = b;
a.x = "whoops"; // no error
b.x; // number at compile time, but string at runtime
b.x.toFixed(); // works at compile time, error at runtime
看看B
如何被认为是A
的一个子类型,在你尝试将错误的东西写入其属性之前这是好的。人们倾向于不这样做,所以语言维护者不管它,因为防止这个问题是困难的并且真的有限(你真的想要只写属性吗?)。
在您的情况下,您的子类正在调用超类的构造函数方法来编写(更宽)属性,即使该属性已被子类缩小。这是同一个问题。
因此,这是解决特定问题的可能方法:使用泛型来指定实际约束,以便缩小/扩展仅在您期望的位置发生:
interface FruitInterface<T extends FruitList> {
fruit: T;
}
abstract class Fruit<T extends FruitList = FruitList> implements FruitInterface<T> {
fruit: T;
constructor(fruit: T) {
this.fruit = fruit;
}
}
interface AppleConstructor {
new(fruit: FruitList.APPLE): AppleInterface;
}
interface AppleInterface extends Fruit<FruitList.APPLE> {
}
class Apple extends Fruit<FruitList.APPLE> implements AppleInterface {
constructor(fruit: FruitList) {
super(fruit); // now error as you expect
}
}
要修复上述错误,您应该将构造函数更改为仅采用FruitList.APPLE
。
希望有所帮助;祝好运!
以上是关于TypeScript在静态实现中推断出更通用的类型的主要内容,如果未能解决你的问题,请参考以下文章