我敢肯定这比我想象的要简单,但我真的什么也没找到。用一个简单的场景更容易解释:
const cookbook: CookBook = {
ingredients: {
"tomato": { vegetal: true },
"cheese": { vegetal: false },
"lettuce": { vegetal: true },
},
recipes: {
"pizza": ["tomato", "cheese"],
"salad": ["tomato", "lettuce"]
}
}
type CookBook = {
ingredients: {[key: string]: {vegetal: boolean}},
recipes: {[key: string]: string[]}
}
可以看到在recipes属性中,values表示的是成分列表,也就是单纯的字符串。我如何指示这些字符串应该是配料对象的键?
...
recipes: {
// This should not be valid because "pineapple"
// is not a key in ingredients property.
"pizza": ["tomato", "cheese", "pineapple"],
...
}
}
你可以写一个带有泛型参数的函数
const cookbook = createCookBook({
ingredients: {
"tomato": { vegetal: true },
"cheese": { vegetal: false },
"lettuce": { vegetal: true },
},
recipes: {
"pizza": ["tomato", "cheese"],
"salad": ["tomato", "lettuce"]
}
})
const createCookBook =
<T extends CookBook & IsValidCookBook<T>>
(cookbook: Narrow<T>) => cookbook
type IsValidCookBook<T extends CookBook> = {
ingredients: T['ingredients'],
recipes: { [K in keyof T['recipes']]: (keyof T['ingredients'])[] }
}
type Narrow<T> = {
[K in keyof T]
: K extends keyof [] ? T[K]
: T[K] extends (...args: any[]) => unknown ? T[K]
: Narrow<T[K]>
};
我们正在根据推断的
ValidCookBook
对象构建一个有效的Cookbook
对象并比较两者。这可以在 IDE 中提供良好的开发人员体验。
Narrow
用于防止 TS 扩大食谱中的成分数组,尽管现在可能有更清洁的方法来做到这一点。