gpt4 book ai didi

typescript - 使用扩展泛型时 `keyof T` 和 `Extract` 有什么区别?

转载 作者:行者123 更新时间:2023-12-05 05:09:13 28 4
gpt4 key购买 nike

假设我有一个为一组数据定义有效值的接口(interface):

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
} ~~~~~
}

Argument of type '"bar"' is not assignable to parameter of type 'Extract<keyof T, string>'. ts(2345)

还值得注意的是,在第二种情况下,如果 Foo 则不会抛出错误。直接使用而不是使用扩展泛型:

class ExtendedGetter extends Getter<Foo> { ... }

为什么会这样?

Extract<keyof T, string> 之间有什么区别?和 keyof T导致错误?

最佳答案

看起来这种行为被认为是一个错误(参见 microsoft/TypeScript#24560 ),但我没有看到任何迹象表明它会在不久的将来得到修复。

但我倾向于将其归为编译器无法为未解析的 conditional types 赋值的类别。如果你有一个像 T extends U ? X : Y 这样的条件类型,并且 TU 是未解析的泛型类型或依赖于未解析的泛型类型,那么编译器不会做太多分析来验证是否有一些值可以分配给它;它主要只是拒绝分配:

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 开始是这样。我看到 a pull request 可能会改善这一点,并且可能会解决您的代码问题,但我只是在猜测,我不知道它何时或是否会成为语言。

只要说 Extract<keyof T, string>T 是一个未解析的泛型时编译器可能很难推理(因为 Extract utility type 是作为条件类型的 implemented)。请注意,一旦 T 被解析为具体类型,如 Foo ,然后 Extract<keyof T, string> 将由编译器评估为具体类型 "bar" 并且没有问题,如您所见。


所以,变通办法。正如您所指出的,您可以做的一件事就是只使用 keyof T 而不是 Extract<keyof T, string>keyof T 类型已知可从 "bar" 分配,尽管它是通用的……编译器能够对未解析的通用类型进行一些推理;当类型是有条件的时,这样做会更糟。如果这对你有用,那就太好了。但是如果你想使用 Extract<keyof T, string> ...

我会使用 type assertion 。当您知道编译器不知道的值的类型时,类型断言很有用。在这种情况下,您确定 "bar" 将可分配给 Extract<keyof T, string> ,因为 "bar" 可分配给 stringkeyof T 。面对现实吧,您比编译器更聪明……类型断言是您吹嘘自己高超智慧的好方法:

class ExtendedGetter<T extends Foo> extends Getter<T> {
use() {
this.get("bar" as Extract<keyof T, string>); // I'm smarter than the compiler 🤓
}
}

当然,应该谨慎使用类型断言,因为如果您的断言错误并欺骗了编译器,那么您可能会在运行时遇到一些不愉快的意外。但在这种情况下,您可以非常确定断言始终有效。


好的,希望对你有帮助。祝你好运!

Link to code

关于typescript - 使用扩展泛型时 `keyof T` 和 `Extract<keyof T, string>` 有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57713166/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com