假设我有一个接口,它定义了一组数据的有效值:
interface Foo {
bar: boolean;
}我希望一个类能够用一个方法来公开数据。我发现如果使用keyof T来定义键,效果会很好:
abstract class Getter<T> {
private data: T;
get<K extends keyof T>(key: K): T[K] {
return this.data[key];
}
abstract use(): void;
}
class ExtendedGetter<T extends Foo> extends Getter<T> {
use() {
this.get('bar'); // OK
}
}但是,将键限制为只接受带有Extract<keyof T, string>的字符串会导致错误:
abstract class Getter<T> {
private data: T;
get<K extends Extract<keyof T, string>>(key: K): T[K] {
return this.data[key];
}
abstract use(): void;
}
class ExtendedGetter<T extends Foo> extends Getter<T> {
use() {
this.get('bar'); // ERROR
} ~~~~~
}类型“bar”的参数不能分配给'Extract‘类型的参数。ts(2345)
还值得注意的是,在第二个场景中,如果直接使用Foo而不是使用扩展泛型,则不会引发错误:
class ExtendedGetter extends Getter<Foo> { ... }这一切为什么要发生?
导致错误的Extract<keyof T, string>和keyof T之间有什么区别?
发布于 2019-08-30 19:16:17
看起来,这种行为被认为是一个bug (请参阅微软/打字稿#24560),但我没有看到任何迹象表明它将在不久的将来修复。
但我倾向于将其归入编译器无法为未解决的条件类型分配值的类别。如果您有像T extends U ? X : Y这样的条件类型,并且T或U是未解决的泛型类型,或者依赖于未解析的泛型类型,那么编译器不会做太多的分析来验证某个值是否可以分配给它;它只是拒绝赋值:
function unresolved<T extends string>() {
const x: [T] extends [string] ? number : number = 1; // error!
const y: string extends T ? number : number = 1; // error!
}在这种情况下,即使这两种条件类型几乎都必须计算为number,编译器也不能确定将1分配给这些类型的变量是安全的,至少在TypeScript 3.6时是这样。我看到拉扯请求可能会改进这一点,并且可能会解决您的代码问题,但我只是在推测,我不知道它何时或是否会将其转换为语言。
可以这么说,当Extract<keyof T, string>是一个未解决的泛型时,编译器很难对T进行推理(因为实用程序类型是作为条件类型的实施 )。注意,一旦T被解析为一个具体的类型,比如Foo,那么Extract<keyof T, string>就会由编译器计算成具体的"bar"类型,就像您看到的那样,没有问题。
所以,解决办法。正如您所提到的,您可以做的一件事就是只使用keyof T而不是Extract<keyof T, string>。keyof T类型已知可从"bar"分配,尽管它是通用的.编译器能够对未解决的泛型类型进行一些推理;只是在类型是有条件的情况下,这样做要糟糕得多。如果这对你有用的话,很好。但如果你想用Extract<keyof T, string>..。
我会用类型断言。当您知道编译器不知道的值的类型时,类型断言非常有用。在这种情况下,您可以确定"bar"可以分配给Extract<keyof T, string>,因为"bar"可以同时分配给string和keyof T。面对现实,你比编译器聪明.类型断言是一种吹嘘自己卓越智慧的好方法:
class ExtendedGetter<T extends Foo> extends Getter<T> {
use() {
this.get("bar" as Extract<keyof T, string>); // I'm smarter than the compiler
}
}当然,应该谨慎地使用类型断言,因为如果您对断言错误并对编译器撒谎,那么在运行时可能会有一些令人不快的意外。但是在这种情况下,您可以非常肯定断言总是有效的。
好吧,希望能帮上忙。祝好运!
https://stackoverflow.com/questions/57713166
复制相似问题