采用以下代码片段:
type A = {a: unknown}
type TakesA<T extends A> = unknown
type B = { b: unknown }
type TakesB<T extends B> = unknown
type AB = A | B
type TakesAB<T extends AB> = T extends A ? TakesA<T> : TakesB<T>
我收到错误类型“T”不满足约束“B”。类型“AB”不可分配给类型“B”
。但是,条件类型不应该在 else 子句中将 T
的类型缩小为 B
吗?为什么会发生这种行为?我应该如何解决它?
最佳答案
这是同一问题的另一个(更简单的)示例:
type Example<T extends string | number> = T extends number
? T['toFixed']
: T['length']; /*
~~~~~~~~~~~
Type '"length"' cannot be used to index type 'T'.(2536) */
我们期望 TypeScript 在 false/else 分支中将 T
缩小为 string
,但这并没有发生(从 TS 版本 4.9.3 开始)
至少)。
这在 GitHub 问题 microsoft/TypeScript#29188 中进行了解释。 :
jack-williams commented on Jan 3, 2019:
Conditional types do not produce substitution types for the false branch of the conditional (a.k.a do not narrow in the false branch).
There was an attempted fix here: #24821, however this was closed.
...
weswigham commented on Jan 3, 2019:
@jack-williams had identified the core of the issue. We use "substitution" types internally to track the constraints applied to a type within the
true
branch of a conditional, however we do no such tracking for thefalse
branch. This means that you can't actually bisect a union type with a conditional right now, as @lodo1995 points out, you must chain two conditions and invert the check so your logic is in thetrue
branch instead.Part of the reason why we didn't move forward with #29011 (other than one of the relations I identified not holding up under scrutiny) is that tracking falsified constraints with substitution types kinda works... but when you perform the substitution, the information is lost, since we do not currently have a concept of a "negated" type (I mitigated this a little bit by remateriaizling the substitutions that tracked negative constraints late, but that's a bit of a hack). We cannot say that the given
T extends string ? "ok" : T
that the type ofT
in the false branch is aT & ~string
, for example - we do not have the appropriate type constructors currently.We regularly bring up how we really do probably need it for completeness, but the complexity "negated" types bring is... large? At least that's what we seem to think - it's not immediately obvious that a
~string
is an alias for "any type except those which are or extend string", and therefore that a~string & ~number
is "any type except strings or numbers" (note how despite the use of&
, the english you read as used the word "or").So we're very aware of what needs to be done to make this work better... we're just having trouble convincing ourselves that it's "worth it".
所以 - 总而言之 - the advice来自Dimava是合理的,您应该使用这种类型,并为最终的“无法访问”分支提供无条件的 never
结果:
type TakesAB<T extends AB> = T extends A
? TakesA<T>
: T extends B
? TakesB<T>
: never;
关于Typescript:区分条件类型中的泛型联合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74575042/