我想创建一些常量来定义具有子类别的类别层次结构。然后,这些将被重用于在应用程序中呈现不同的页面,但是通过将它们定义为一个位置的常量,我可以快速搭建页面基架。
例如,给定的类别如下:
const CATEGORIES = [
{
id: 'diet',
name: 'Diet',
color: 'green',
},
{
id: 'energy',
name: 'Energy',
color: 'yellow',
},
...
]
但是现在我想用TypeScript键入这些对象。我可以使用as const
语法来推断类别类型:
{
id: 'diet' | 'energy' | ...
name: 'Diet' | 'Energy' | ...
color: 'green' | 'yellow' | ...
}
这很好。(虽然我真的不需要name/color
特异性。
但是在定义子类别时,我遇到了一个问题......
const SUBCATEGORIES = [
{
id: 'red_meat',
category: 'diet',
name: 'Red Meat',
emoji: '🥩',
},
...
]
如果我在SUBCATEGORIES
对象上使用as const
,那么category
键就没有类型安全。我很容易拼错'deit'
,永远不知道。
但是如果我使用这样的声明:
const SUBCATEGORIES: Array<{
id: string
category: typeof CATEGORIES[number]['id']
...
}> = [
...
]
然后我以后无法再从子类别推断出id
键字符串,因为它们将具有一种string
类型。
有没有办法在TypeScript中定义这些类型的元数据结构,以便在as const
和定义的类型之间有一些中间地带?如何在键入其余键时将id
键推断为常量?
我愿意将它们定义为对象而不是数组,但我似乎无法找到一种有帮助的方法。
在这种情况下,我通常会创建一个帮助函数,该函数验证值是否可以分配给类型,而无需将其扩大到该类型。 假设CATEGORIES
看起来像
const CATEGORIES = [
{
id: 'diet',
name: 'Diet',
color: 'green',
},
{
id: 'energy',
name: 'Energy',
color: 'yellow',
},
// ...
] as const;
那么帮助程序函数asSubcategories
可能如下所示:
const asSubcategories = <S extends ReadonlyArray<{
id: string;
category: typeof CATEGORIES[number]['id'];
name: string;
emoji: string;
}>>(s: S) => s;
在这里asSubcategories
实际上只是返回其输入而不更改它,但它只接受可分配给您关心的类型S
的泛型类型参数。 然后你这样称呼它:
const SUBCATEGORIES = asSubcategories([
{
id: 'red_meat',
category: 'diet',
name: 'Red Meat',
emoji: '🥩',
},
//...
] as const);
由于没有错误,您没有拼错category
,并且SUBCATEGORIES
的类型仍然是
/* const SUBCATEGORIES: readonly [{
readonly id: "red_meat";
readonly category: "diet";
readonly name: "Red Meat";
readonly emoji: "🥩";
}] */
如果你拼错了东西,你应该得到一个大错误:
const BADSUBCATEGORIES = asSubcategories([
{
id: 'read_met',
category: 'deit',
name: 'Read Met',
emoji: '🍔',
},
//...
] as const); // error!!
/* Types of property 'category' are incompatible.
Type '"deit"' is not assignable to type '"diet" | "energy"'
*/
耶! 有一些方法可以使用帮助程序函数来缩小某些属性的范围,同时扩大其他属性,但希望上述内容足以帮助您继续操作。祝你好运!
操场链接到代码