Как определить Generi c Типы недвижимости? - PullRequest
0 голосов
/ 09 января 2020

Я не могу понять, как вывести тип свойства generi c на основе типа объекта generi c, на котором он находится. В следующем примере, как я могу сказать, что Something.aProp должен соответствовать типу Something's U.obj.prop?

interface Prop {
  a: number;
}
interface FancyProp extends Prop {
  b: number;
}

interface Obj<T extends Prop> {
  prop: T;
}

interface FancyObj extends Obj<FancyProp> {}

interface Parent<T extends Obj<any>> { // <-- the <any> here seems wrong too
  obj: T;
}

interface FancyParent extends Parent<FancyObj> {
  fancy: number;
}

class Something<U extends Parent<any>> {
  aProp: typeof U.obj.prop;
}

Т.е. Something<Parent>.aProp должен иметь тип Prop, а Something<FancyParent>.aProp типа FancyProp?

1 Ответ

0 голосов
/ 10 января 2020

Для вашего основного вопроса, способ поиска типа значения свойства по типу объекта T и типу ключа K заключается в использовании типов поиска, или индексированных типов доступа через синтаксис скобки T[K]. Поэтому, если вы хотите найти тип свойства "prop" -keyed свойства "obj" -keyed объекта типа U, вы должны написать этот тип как U["obj"]["prop"].

Обратите внимание, что точечный синтаксис не работает для типов, даже если ключевые типы являются строковыми литералами. Было бы неплохо, если бы U.obj.prop был синонимом для U["obj"]["prop"] в системе типов, но, к сожалению, , к сожалению, этот синтаксис столкнулся бы с пространствами имен , поскольку могло бы существовать пространство имен с именем U, с подпространством именованных obj с экспортированным типом с именем prop, а затем U.obj.prop будет ссылаться на этот тип.


Ваши комментарии о any не совсем неправильно использовать X extends Y<any>, когда параметр типа Y<T> T имеет generi c ограничение , но оно может быть менее безопасным, чем вы можете получить. Если тип Y<T> относится к T ковариантным способом , то вместо any можно использовать ограничение generi c.

Это означает, например, что Parent<T extends Obj<any>> можно заменить на Parent<T extends Obj<Prop>>, а U extends Parent<any> можно заменить на U extends Parent<Obj<Prop>>.


Эти изменения дают вам код как это:

interface Parent<T extends Obj<Prop>> {
    obj: T;
}

class Something<U extends Parent<Obj<Prop>>> {
    aProp: U['obj']['prop'];
    constructor(u: U) {
        this.aProp = u.obj.prop;
    }
}

Я также добавил конструктор к Something, потому что свойства класса должны быть инициализированы , и я хотел показать, что aProp можно присвоить со значением из u.obj.pop когда u является U.

И это должно работать так, как вы ожидаете:

interface PlainObj extends Obj<Prop> { }
interface PlainParent extends Parent<PlainObj> { }
new Something<PlainParent>({ obj: { prop: { a: 1 } } }).aProp.a; // number

interface FancyObj extends Obj<FancyProp> { }
interface FancyParent extends Parent<FancyObj> {
    fancy: number;
}
new Something<FancyParent>({ obj: { prop: { a: 1, b: 2 } }, fancy: 3 }).aProp.b; // number

Хорошо, надеюсь, это поможет; удачи!

Детская площадка ссылка на код

...