我需要从 PALETTE 对象派生类型
ColorVariant
。预期结果是:
type ColorVariant = "common.black" | "common.white" | "primary.25" | "primary.50" | "primary.100" | "error.25" | "error.50" | "error.100" | "success.25" | "success.50" | "success.100"
实际结果是:
type ColorVariant = never
代码:
const PALETTE = {
common: {
black: "#000",
white: "#fff",
},
primary: {
25: "#FAFAFF",
50: "#F5F5FF",
100: "#E7E7FC",
},
error: {
25: "#FFFBFA",
50: "#FEF3F2",
100: "#FEE4E2",
},
success: {
25: "#F6FEF9",
50: "#ECFDF3",
100: "#D1FADF",
},
} as const;
export type Colors = keyof typeof PALETTE;
export type ColorOptions = (typeof PALETTE)[Colors];
export type Tone = keyof ColorOptions;
export type ColorVariant = `${Colors}.${Tone}`;
我确实知道发生这种情况是因为
common
和 primary, error, success
中的键不同......不幸的是,我不明白如何解决它。请帮忙
您实际上需要迭代
K
中的每个键 keyof Palette
,并为每个这样的键计算 模板文字类型 `${K}.${keyof Palette[K]}`
,然后计算所有这些键的 union。您可以使用 keyof Palette
上的映射类型来完成此操作,然后使用
keyof Palette
索引到。
这被称为 分布式对象类型 ,在 microsoft/TypeScript#47109 中创造,当你有一个类似键的联合
{[K in KK]: F<K>}[KK]}
和一个类型函数 KK
时,它看起来像 F<>
。它在 F<>
中的联合上分配
K
类型函数。因此,如果 KK
是 K1 | K2 | K3
,那么 {[K in KK]: F<K>}[KK]
就是 F<K1> | F<K2> | F<K3>
。
从概念上来说
ColorVariant
它看起来像
type ColorVariant = { [K in keyof Palette]:
`${K}.${keyof Palette[K]}` // error!
}[keyof Palette];
但是有一个问题,编译器没有注意到
keyof Pallete[K]
始终可以通过模板文字类型进行序列化。 keyof
运算符可以产生任何 PropertyKey
,即 string | number | symbol
。但只有 string | number
是可序列化的,而 symbol
则不是。因此,您需要根据 Exclude
的结果
symbol
keyof
来确保编译器可以接受:
type ColorVariant = { [K in keyof Palette]:
`${K}.${Exclude<keyof Palette[K], symbol>}`
}[keyof Palette];
这就变成了
/* type ColorVariant = "common.black" | "common.white" |
"primary.25" | "primary.50" | "primary.100" |
"error.25" | "error.50" | "error.100" |
"success.25" | "success.50" | "success.100"
*/
随心所欲。