首页 > 解决方案 > 如何在通用占位符上使用提取

问题描述

bar 类的extend守卫没有在 foo 类上强制执行。

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

酒吧中的 get 和 get2 调用。

class foo<T> {
    get(a: Extract<keyof T, string>) {
        return '';
    }
}

interface IAttrs {
    bar: boolean;
}

class bar<T extends IAttrs> extends foo<T>{

    constructor() {
        super();

        this.get('bar')
        this.get2('bar')
    }
 get2(a: Extract<keyof T, string>) {
        return '';
    }

}

class foobar extends foo<IAttrs>{

    constructor() {
        super();

        this.get('bar')
}

}


我希望 bar 和 foobar 类都能编译;但是 bar 类有以下错误:

我正在使用打字稿 3.2.2

标签: typescriptgenerics

解决方案


主要问题是 whenT是一个未解析的泛型类型参数,编译器不会(从 TS3.5 开始)执行它需要的类型分析,以便识别即使在被约束时"bar"也可分配给 extend 。 是一种条件类型,编译器通常会推迟对未解析条件类型的大多数分析。也许这个特殊的用例可以作为建议提交到 GitHub,但我怀疑它会被优先考虑。Extract<keyof T, string>TIAttrsExtract

所以我们需要要么使用已解析的条件类型,要么我们需要在非条件类型中使用未解析的类型参数。

  • 已解析的条件类型:由于keyof T包含所有keyof IAttrs,所以类型keyof T等价于keyof T | keyof IAttrs。所以Extract<keyof T | keyof IAttrs, string>相当于有问题的类型。但由于Extract分布的,它会被评估为Extract<keyof T, string> | Extract<keyof IAttrs, string>。最右边的成分是一个已解决的条件类型,它将被急切地评估为 just "bar"。所以现在我们有了Extract<keyof T, string> | "bar". 未解决的条件类型仍然存在,但现在"bar"明确地属于联合......并且错误应该消失。

  • Unresolved non-conditional type:该Extract类型是在 TS2.8 中引入的,带有条件类型。它过滤带有约束的联合类型。在 TS2.8 之前,如果我们想约束这样的类型,我们只需要使用交集。过去,联合上的交叉点也很混乱,并且("foo" | "bar") & ("bar" | "baz")会评估为("foo" & "bar") | ("foo" & "baz") | "bar" | ("bar" & "baz"). 如今,尽管它根据需要减少到 just "bar"。幸运的是,即使是未解析的类型参数也会在交集中进行分析......也就是说,如果X已知可分配给AB,则X已知可分配给A & B。所以我们可以更改Extract<keyof T, string>(keyof T) & string,错误也应该消失。

让我们看看一些修改后的代码,它可以同时做这两个事情:

class Foo<T> {
  // intersection
  get(a: keyof T & string) {
    return "";
  }
}

interface IAttrs {
  bar: boolean;
}

class Bar<T extends IAttrs> extends Foo<T> {
  constructor() {
    super();
    this.get("bar");
    this.get2("bar");
  }
  // resolved conditional
  get2(a: Extract<keyof T | keyof IAttrs, string>) {
    return "";
  }
}

class FooBar extends Foo<IAttrs> {
  constructor() {
    super();

    this.get("bar");
  }
}

游乐场链接

并且所有编译都没有错误。好的,希望有帮助;祝你好运!


推荐阅读