每当我认为我已经理解了泛型,我就会想出一个证明我错了的任务,这就是我想要的:
Playground
type tFruits = 'apple' | 'orange' | 'pear';
type tCars = 'peugot' | 'mercedes'| 'nissan';
type tFlowers = 'rose' | 'tulip' | 'violet';
type tAssets = {
image: {},
text: {},
}
type tFruitAssets = tAssets & { id: tFruits }
type tCarAssets = tAssets & { id: tCars }
type tFlowerAssets = tAssets & { id: tFlowers }
const createAssets = <T, U>(ids: U[]):T[] => {
const assets:T[] = [];
for (const dataId of ids){
// Typescript doesn't like this:
assets.push({
id: dataId,
image:{},
text:{},
});
}
return assets;
}
const fruitAssets = createAssets<tFruitAssets, tFruits>(['apple','orange','pear']);
const carAssets = createAssets<tCarAssets, tCars>(['peugot','mercedes','nissan']);
const flowerAssets = createAssets<tFlowerAssets, tFlowers>(['rose','tulip','violet']);
Typescript不喜欢我试图推送到createAssets函数中的assets数组的对象,它说:
Argument of type '{ id: U; image: {}; texts: {}; }' is not assignable to parameter of type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to '{ id: U; image: {}; texts: {}; }'.
有没有人能给我解释一下我必须怎么改变功能?或者是不可能的?
3条答案
按热度按时间l7mqbcuq1#
因为
T
可能是其他类型,如tAssets & { id: tFruits } & { anotherId: tFruits }
,它不像createAssets
那样与tAssets & { id: tFruits }
兼容。createAssets
说它可以从U[]
创建T[]
,但实际上它只能从U[]
创建(tAssets & { id: U })[]
。如果T
包含anotherId
,createAssets
将不会返回它,这可能会导致错误。createAssets
所做的是从U[]
创建(tAssets & { id: U })[]
,因此其类型应为:hof1towb2#
当你可以Assert它并暂时结束它的时候,
这实际上是不合理的,因为调用者选择了
T
和U
类型,这意味着它通过了类型检查:您可以通过向泛型参数添加约束来轻松地使其更安全。
但是,现在您必须使用
as unknown as T
,因为TypeScript看到了有人可能使用这意味着将
t
推到assets
是不合理的,因为您将丢失那个额外的属性。如果您真的想这样做,您还可以通过稍微更改约束来防止这种情况发生...不幸的是,在所有这些都准备就绪的情况下,Assert仍然是必要的,您可以尝试使用
map
使它更简洁一些(去掉双重Assert):Playground
q5lcpyga3#
10分钟后我想到了这个:
Typescript现在很满意,但我觉得它更像是一种变通方法,而不是一个适当的解决方案。我在这里自欺欺人吗?