以此示例代码为例:
interface Cat {
fur: string
food: string
}
const cats: Array<Cat> = [
{
fur: 'brown',
food: 'fish'
},
{
fur: 'white',
food: 'biscuits'
}
];
interface Cols {
name: string
title: string
}
const cols: Array<Cols> = [
{
name: 'fur',
title: 'Fur colour'
},
{
name: 'food',
title: 'Favourite food'
}
];
cats.map(cat => {
cols.map(col => {
console.log(`${col.title} - ${cat[col.name]}`); // errors here
});
});
它cat[col.name]
错误:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Cat'.
No index signature with a parameter of type 'string' was found on type 'Cat'.(7053)
我可以通过向cat
添加any
来抑制该错误,但这违背了重点。键入此内容的正确方法是什么?
这是因为 TypeScript 将col.name
视为字符串,而Cat
接口仅包含两个可能的键(不是任何字符串作为键)。理论上,col.name
可以返回对象cat
中不作为键存在的任何字符串。
由于您知道col.name
必须是Cat
接口的键,因此您可以这样提示 TypeScript:
// Tell TS that col.name must be a keyof the Cat interface
cat[col.name as keyof Cat]
因此,像这样更新代码将导致 TypeScript 识别出col.name
确实在Cat
接口中返回了有效的密钥:
cats.map(cat => {
cols.map(col => {
console.log(`${col.title} - ${cat[col.name as keyof Cat]}`);
});
});
请参阅 TypeScript Playground 上的概念验证示例。
专业提示:假设您没有显式接口Cat
而只有cat
对象,您还可以执行以下操作:
// Tell TS that col.name must be a keyof the typeof `cat` object
cat[col.name as keyof typeof cat]
另一种方法是对Cat
接口中的属性使用enum
,这样您就不必手动强制转换它:因为 TypeScript 可以推断出类型。此方法在不需要手动转换col.name
类型的情况下要详细得多(请参阅此处的 Playground 示例):
// Declare an enum for properties in `Cat` interface
enum CatProperty {
FUR = 'fur',
FOOD = 'food',
}
然后,在为cols
定义数组时,您将需要使用枚举:
const cols: Array<Cols> = [
{
name: CatProperty.FUR,
title: 'Fur colour'
}
];
这应该导致 TypeScript 能够识别col.name
必须是fur | food
的值,它们都存在于Cat
接口中,因此不需要强制转换:
cats.map(cat => {
cols.map(col => {
console.log(`${col.title} - ${cat[col.name]}`);
});
});