给定一个定义为可能命名配置的常量记录的简单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
引用为特定(非泛型)类型,那么它应该计算为keyof typeof configs
中每个K
的原始ConfigDeg<K>
的并集。也就是说,您需要在联合keyof typeof configs
上分布ConfigDef<K>
。一种方法是编写一个分布式对象类型,如microsoft/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>
。
Playground链接到代码