Как гарантировать, что универсальный тип может принимать универсальный тип? - PullRequest
0 голосов
/ 21 июня 2019

Извиняюсь за запутанный заголовок, поскольку я не знаю, как его выразить ясно.
Я пытаюсь найти способ создания универсального типа, который будет использоваться позже для передачи универсального типа.

Чтобы устранить путаницу, учитывая эти два интерфейса:

interface Field<T> {
  value: T;
  hasError: boolean;
}

interface Record {
  id: number;
  name: string;
}

И эти наследования Field

interface FooField<T> extends Field<T> {
  foo: () => void;
}

interface BarField<T> extends Field<T> {
  bar: () => void;
}

Я хотел бы генерировать новые типы Recordследующего типа:

interface FooRecord {
  id: FooField<number>;
  name: FooField<string>;
}

interface BarRecord {
  id: BarField<number>;
  name: BarField<string>;
}

Наиболее интуитивный, но неправильный способ сделать это будет следующим:

type MagicRecord<XField extends Field> = {
  [K in keyof Record]: XField<Record[K]>;
};

type FooRecord = MagicRecord<FooField>;
type BarRecord = MagicRecord<BarField>;

Как я могу сделать это, если возможно, правильнос машинописью?

1 Ответ

0 голосов
/ 21 июня 2019

Вы хотите более родственные типы , которые не поддерживаются в TypeScript с версии 3.5.Все, что вы здесь сделаете, будет обходным путем, включающим некоторое количество дублирования кода.

Вот один из возможных обходных путей.Прежде всего, обратите внимание, что я не собираюсь использовать имя Record, поскольку оно совпадает с именем псевдонима встроенного типа .Я переключусь на MyRecord:

interface MyRecord {
  id: number;
  name: string;
}

Итак, давайте создадим справочную таблицу FieldLookup<T>, которая сопоставляет имя универсального типа с самим универсальным типом, указанным с помощью T.Эта таблица будет нуждаться в одной записи для каждого подтипа Field<T>, который вы хотите поддерживать:

interface FieldLookup<T> extends Record<keyof FieldLookup<any>, Field<T>> {
  Field: Field<T>;
  FooField: FooField<T>;
  BarField: BarField<T>;
}

Затем мы можем определить MagicRecord<XField>, который принимает имя универсального поля в виде строкового литерала:

type MagicRecord<XField extends keyof FieldLookup<any>> = {
  [K in keyof MyRecord]: FieldLookup<MyRecord[K]>[XField]
};

type FooRecord = MagicRecord<"FooField">;
type BarRecord = MagicRecord<"BarField">;

Так что это работает.Мы могли бы пойти немного дальше и сделать так, чтобы вам не нужно было использовать имена типов или заботиться о них, реализовав обратный поиск для FieldLookup:

type ReverseLookup<F extends Field<any>> = {
  [K in keyof FieldLookup<any>]: FieldLookup<any>[K] extends F ? K : never
}[keyof FieldLookup<any>];

И тогда MagicRecord<XField> будетиспользовать в качестве исходной версии, за исключением того, что XField должен быть конкретного типа:

type MagicRecord2<XField extends Field<any>> = MagicRecord<
  ReverseLookup<XField>
>;

type FooRecord2 = MagicRecord2<FooField<any>>; // FooField<any>, not FooField
type BarRecord2 = MagicRecord2<BarField<any>>; // BarField<any>, not BarField

Это также работает.

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

Ссылка на код

...