TypeScript class 属性 查找方法类型推断

TypeScript class property lookup method type inference

我正在努力让类型推断与 class 属性 查找一起使用。

我想要的:

class Family <T extends { [name: string]: Animal }> {
  public members: T

  public lookup <K extends keyof T>(name: K): T[K] {
    return this.members[name]
  }
}

class Animal {}
class Cat extends Animal {}
class Dog extends Animal {}

const cat = new Cat
const dog = new Dog

const foo = new Family
foo.members = { kitty: cat, bob: dog }
const result1 = foo.lookup('missing') // I want this to fail for missing key. It doesn't.
const result2 = foo.lookup('kitty') // I want this has inferred type Cat. It doesn't.

我相信以上是可以实现的,因为我得到的功能版本如下所示

function lookup <O, K extends keyof O> (obj: O, key: K): O[K] {
  return obj[key]
}

const o = { a:1, b: 'text'}
const r = lookup(o, 'a') // correctly inferred type number
const r2 = lookup(o, 'b') // correctly inferred type string

任何帮助或指南将不胜感激。提前致谢。

变量的类型一般是在声明变量的时候推断出来的,之后一般不会改变(所以foo的类型为Family<{ [name: string]: Animal; >因为在初始化的时候没有其他的可用信息)

如果您可以将 member 传递给构造函数,根据将按预期工作的参数推断 T

class Family <T extends { [name: string]: Animal }> {
  constructor(public members: T) { }

  public lookup <K extends keyof T>(name: K): T[K] {
    return this.members[name]
  }
}

const result1 = foo.lookup('missing') // err
const result2 = foo.lookup('kitty') // cat

Playground Link

向构造函数显式指定类型参数也是一种选择,但不那么方便:


const foo = new Family<{ kitty: Cat, bob: Dog }>()
foo.members = { kitty: cat, bob: dog }
const result1 = foo.lookup('missing') // err
const result2 = foo.lookup('kitty') // cat

Playground Link

现在,如果这些选项都不可行,您可以走更深奥的路线,并使用 custom assertion function。这些自定义断言如果用作顶级语句可以更改变量的类型:

class Family <T extends { [name: string]: Animal }> {
  private members!: T;
  public setMemebers<TNew extends T>(members: TNew): asserts this is Family<TNew> {
    this.members = members;
  }

  public lookup <K extends keyof T>(name: K): T[K] {
    return this.members[name]
  }
}

class Animal {}
class Cat extends Animal {}
class Dog extends Animal {}

const cat = new Cat
const dog = new Dog

const foo: Family<any> = new Family()
foo.setMemebers({ kitty: cat, bob: dog });
const result1 = foo.lookup('missing') // err
const result2 = foo.lookup('kitty') // cat

Playground Link