Вы хотите более родственные типы , которые не поддерживаются в 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
Это также работает.
Хорошо, надеюсь, это поможет.Удачи!
Ссылка на код