我试图用TypeScript重写一些C#代码,但没有用。除非我直接提供基类,否则无法推断响应类型。
class CommandParameter {
readonly value: string = "value";
}
class Command<T> {}
class SubCommand<T> extends Command<T> {}
class Client {
execute<T>(command: Command<T>): T {
return null as T;
}
}
//TS2339: Property 'value' does not exist on type 'unknown'.
const valueError = new Client().execute(new SubCommand<CommandParameter>()).value; //<--error
const valueOk = new Client().execute(new Command<CommandParameter>()).value;
使用Typescript是否有可能实现所需的行为(使推理为子类工作)?是否有任何类似的替代模式?
1条答案
按热度按时间nbnkbykc1#
TypeScript的类型系统很大程度上是结构化的,而不是nominal。类型是通过它们的形状/结构来比较的,而不是通过它们的名称/声明。
在结构上彼此相同,并且与空对象类型相同,并且在结构上独立于它们的generic类型参数。因此
Command<string>
和SubCommand<number>
以及Command<Date>
和SubCommand<boolean>
都是 * 相同类型 *。这使得推理表现不佳;有时候编译器确实可以通过声明的模式匹配从
Command<T>
中推断出T
,就好像它有一个名义类型系统一样,但是即使这样做也是非常脆弱的,一旦编译器执行了结构比较,那么就无法从{}
推断T
。这个推断问题在TypeScript FAQ条目中描述为什么类型推断不能在此接口上工作:interface Foo<T> { }
?一般来说,修复方法是使任何泛型类型在结构上依赖于其泛型类型参数。例如:
现在
Command<T>
有一个T
类型的x
属性,SubCommand<T>
也是这样,因为它继承自Command<T>
。所以现在这些类型不再与{}
相同,而是与{x: T}
相同。从{}
推断T
通常是不可能的。但是从{x: T}
推断T
非常简单。现在一切都正常了:显然,给一个类一个类型为泛型类型参数的属性并不总是适用的,但希望您实际上在类中的某个地方处理
T
类型的值,以便Command<T>
在结构上 * 以某种方式 * 依赖于T
。或者你可能不得不用dummy/phantom属性来模拟nominal类型,但是这些都超出了这个问题的范围,所以我在这里就不多说了。Playground代码链接