Вывод типа метода поиска свойств класса TypeScript - PullRequest
1 голос
/ 01 мая 2020

Я пытаюсь получить вывод типа для работы с поиском свойств класса.

Что я хочу:

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

Любая помощь или руководство будут оценены. Заранее спасибо.

1 Ответ

1 голос
/ 01 мая 2020

Тип переменной обычно выводится, когда переменная объявляется, и обычно не изменяется после (поэтому 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

Теперь, если ни один из этих вариантов это невозможно, вы можете go более эзотерический c маршрут и использовать пользовательскую функцию подтверждения . Эти пользовательские утверждения, если они используются в качестве оператора верхнего уровня, могут изменять тип переменной:

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

...