考虑以下示例:
function identity<T>(arr: T[]) { return arr }
identity(["a", "b"])
在这里,泛型类型 T
被推断为 string
,这对我来说很有意义。
但是如果在T
上加了约束(如下图),那么T
会被推断为"a"| “b”
,为什么?
function identity<T extends string>(arr: T[]) { return arr }
identity(["a", "b"])
最佳答案
我学到了一些东西可以分享。这不是一个明确的答案,但值得注意:
什么时候加宽类型参数,来自"a"
至 string
?
https://github.com/microsoft/TypeScript/pull/10676
During type argument inference for a call expression the type inferred for a type parameter T is widened to its widened literal type if:
- all inferences for T were made to top-level occurrences of T within the particular parameter type, and
- T has no constraint or its constraint does not include primitive or literal types, and
- T was fixed during inference or T does not occur at top-level in the return type.
类型参数T
在以下情况下扩大:
-
T
没有约束 - 或其约束不包括原始类型或字面量类型
加宽
<T>(arr:T[])
没有约束,所以类型会变宽。
'a'
= string
无约束版本是如此通用,TS 没有对如何处理字面量做出太多假设。
未加宽
<T extends string>(arr:T[])
具有原始约束且未加宽。
由于受限版本不太通用,因此它保留了它们。请参阅 PR,了解通常希望尽可能多地保留文字。
这也是一个有趣的花絮:
In cases where literal types are preserved in inferences for a type parameter (i.e. when no widening takes place), if all inferences are literal types or literal union types with the same base primitive type, the resulting inferred type is a union of the inferences. Otherwise, the inferred type is the common supertype of the inferences, and an error occurs if there is no such common supertype.
T 的所有推导都是相同基本原始类型 ( "a"
) 的文字类型 ( "b"
, string
)。
const fn = <T extends string>(arr:T[]) => arr
fn(['a', 'b']) // ('a' | 'b')[]
如果推断不是文字类型,则推断类型是公共(public)父类(super class)型,string
.
const fn = <T extends string>(arr:T[]) => arr
fn(['a' as string, "b"]) // widened to common supertype `string`
一般来说,TS 会为给定的输入推断出“最佳”类型。这个函数恰好将字符串文字(作为不可变表达式)传递给 identity
,其返回类型为 T[]
谁的T
受 string
约束.如果它可以保留文字,它就会保留。
您可以看到有大量场景(从该 PR 粘贴),在这些场景中推理可以保留文字,而在哪些地方不能。
- https://github.com/microsoft/TypeScript/pull/10676
- https://github.com/microsoft/TypeScript/pull/11126
我没有关于哪种推理规则组合具体启用此行为的答案,但通常, typescript 会 typescript ,并且通常会尝试产生我们想要的输出。
关于typescript - 为什么在泛型类型上添加约束会改变推理行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73669925/