Учитывая простой Record<string, Record<string, any>>
, определенный как постоянная запись возможных именованных конфигураций, Мне нужен тип, который динамически ограничивает тип конфигурации на основе ключа. Рассмотрим следующий пример:
const configs = {
"fooConfig": {c: "s"},
"barConfig": {c: 1, b: 2}
} as const;
type ConfigDef<k extends keyof typeof configs = keyof typeof configs> = {
a: k,
v: typeof configs[k]
}
const x: ConfigDef = {
a: "fooConfig",
v: {c: 1, b: 2} // this should not work
}
Важно отметить, что для типа ConfigDef
мне нужно, чтобы пользователь мог использовать этот тип неявно, не передавая фактический ключ конфигурации в качестве универсального типа (т.е. ему не нужно использовать явный синтаксис const x: ConfigDef<"fooConfig">
). Возможно ли это, и если да, то как?
Вы можете сделать это с помощью универсальной вспомогательной функции, которая выведет за вас универсальные шаблоны в ConfigDef.
Обратите внимание, что k
— это общий параметр тип, и поэтому он обычно пишется в верхнем регистре как K
, чтобы отличить его от потенциального ценность (например, переменной с именем k
).
Если вы хотите ссылаться на ConfigDef
как на конкретный (не универсальный) тип, то он должен оцениваться как союз вашего исходного ConfigDeg<K>
для каждого K
в keyof typeof configs
. То есть нужно распространятьConfigDef<K>
по союзу keyof typeof configs
. Один из способов сделать это — написать тип объекта дистрибутива, как это было придумано в Майкрософт/TypeScript#47109:
type ConfigDef = { [K in keyof typeof configs]: {
a: K,
v: typeof configs[K]
} }[keyof typeof configs]
Это оценивает
/* type ConfigDef = {
a: "fooConfig";
v: {
readonly c: "s";
};
} | {
a: "barConfig";
v: {
readonly c: 1;
readonly b: 2;
};
} */
и поэтому дает вам поведение, которое вы хотите:
const x: ConfigDef = {
a: "fooConfig",
v: { c: 1, b: 2 } // error!
}
Тип дистрибутивного объекта — это когда вы сразу индексировать в получаете отображаемый тип. Общая форма — {[K in KS]: F<K>}[KS]
, где KS
— ключевой тип. В вашем случае KS
является keyof typeof configs
и F<K>
вашим исходным ConfigDef<K>
.
Вы хотите, чтобы тип
configs.fooConfig.c
былstring
или простоs
? То же самое для другой конфигурации: это буквальный тип 1 и 2 илиnumber
?