使用TypeScript,我定义了WeightedItem
和createWeightedTable
如下:
type WeightedItem<T> = { item: T; weight: number };
const createWeightedTable = <T>(items: WeightedItem<T>[] | T[], options?: Options): WeightedTable<T>
我惊讶地发现这一点:
const weightedItems = [{ item: "a" }];
createWeightedTable(weightedItems)
使TypeScript抱怨以下内容:
Argument of type '{ item: string; }[]' is not assignable to parameter of type 'WeightedItem<string>[] | string[]'.
Type '{ item: string; }[]' is not assignable to type 'WeightedItem<string>[]'.
Property 'weight' is missing in type '{ item: string; }' but required in type 'WeightedItem<string>'.ts(2345)
然而,这些变化不会:
const weightedItems = [{ item: "a", foo: "bar" }];
createWeightedTable(weightedItems);
const weightedItems = [{ weigtht: 1 }];
createWeightedTable(weightedItems);
为什么会这样呢?
为什么TypeScript假设{ item: "a" }
是一个“有缺陷的”WeightedItem
,而不是另一个通用的T
,但对于{ item: "a", foo: "bar" }
或{ weight: 1 }
却不是这样?
编辑:
根据建议,在此处添加了最小可重现示例。
1条答案
按热度按时间tvmytwxo1#
看起来这被认为是TypeScript中的一个bug,正如microsoft/TypeScript#29471中所报告的那样,尽管它已经打开了很长一段时间,并且没有迹象表明它会很快得到解决。
TypeScript的推理算法使用各种启发式方法,例如将“优先级”级别分配给generic * 推理站点 ,可以进行推理的地方,然后使用最高优先级站点来提出推理的候选者。如果该候选者不起作用,编译器通常只是报告错误而不是退回到较低优先级的推理站点。推理算法决不是完美的,并且不是microsoft/TypeScript#30134中描述的正式正确的 * 完整unification 算法,但它在各种真实的世界的代码中表现良好,因此更改它来解决问题可能弊大于利。我的意思是:最好还是假设这不会改变
无论如何,在
WeightedItem<T>[] | T[]
中,类型参数T
有两个推断点,就像WeightedItem<**T₁**>[] | **T₂**[]
一样。看起来编译器给T₁
分配了比T₂
更高的优先级,所有的事情都是这样。你可以通过交换联合成员的顺序来改变这种行为,就像T[] | WeightedItem<T>[]
一样:不幸的是,这将阻止第二个union成员被匹配,因为任何数组都将匹配
T[]
,包括那些匹配WeightedItem<T>[]
的数组:如果你想解决这个问题,你可能需要更明确地指导类型,也许可以使用conditional type:
这是可行的,因为我们只是让
T
被推断为数组元素类型,然后用条件类型UnwrapWeightedItem<T>
计算WeightedTable<>
的类型参数。这并不理想,特别是在函数实现中,编译器更难理解泛型条件类型,但至少你可以从调用方计算类型。Playground链接到代码