首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在使用扩展泛型时,‘`keyof’与`Extract<keyof T、string>`有什么区别?

在使用扩展泛型时,‘`keyof’与`Extract<keyof T、string>`有什么区别?
EN

Stack Overflow用户
提问于 2019-08-29 15:13:58
回答 1查看 2.5K关注 0票数 3

假设我有一个接口,它定义了一组数据的有效值:

代码语言:javascript
复制
interface Foo {
  bar: boolean;
}

我希望一个类能够用一个方法来公开数据。我发现如果使用keyof T来定义键,效果会很好:

代码语言:javascript
复制
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>的字符串会导致错误:

代码语言:javascript
复制
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而不是使用扩展泛型,则不会引发错误:

代码语言:javascript
复制
class ExtendedGetter extends Getter<Foo> { ... }

这一切为什么要发生?

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-08-30 19:16:17

看起来,这种行为被认为是一个bug (请参阅微软/打字稿#24560),但我没有看到任何迹象表明它将在不久的将来修复。

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

代码语言:javascript
复制
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"可以同时分配给stringkeyof T。面对现实,你比编译器聪明.类型断言是一种吹嘘自己卓越智慧的好方法:

代码语言:javascript
复制
class ExtendedGetter<T extends Foo> extends Getter<T> {
  use() {
    this.get("bar" as Extract<keyof T, string>); // I'm smarter than the compiler 
  }
}

当然,应该谨慎地使用类型断言,因为如果您对断言错误并对编译器撒谎,那么在运行时可能会有一些令人不快的意外。但是在这种情况下,您可以非常肯定断言总是有效的。

好吧,希望能帮上忙。祝好运!

链接到代码

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57713166

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档