You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Generic conditional type T extends never ? 'yes' : 'no' resolves to never when T is never. #31751
Search Terms:
never, extends, generic, conditional type Code
type MakesSense = never extends never ? 'yes' : 'no' // Resolves to 'yes'
type ExtendsNever<T> = T extends never ? 'yes' : 'no'
type MakesSenseToo = ExtendsNever<{}> // Resolves to 'no'
type Huh = ExtendsNever<never> // Expect to resolve to 'yes', actually resolves to never
Expected behavior:
Huh should resolve to 'yes', as MakesSense does.
Actual behavior:
Huh resolves to never.
For another variant, you can change ExtendsNever to
type ExtendsNever<T extends never> = T extends never ? 'yes' : 'no'
This is the expected behavior, ExtendsNever is a distributive conditional type. Conditional types distribute over unions. Basically if T is a union ExtendsNever is applied to each member of the union and the result is the union of all applications (ExtendsNever<'a' | 'b'> == ExtendsNever<'a' > | ExtendsNever<'b'>). never is the empty union (ie a union with no members). This is hinted at by its behavior in a union 'a' | never == 'a'. So when distributing over never, ExtendsNever is never applied, since there are no members in this union and thus the result is never.
If you disable distribution for ExtendsNever, you get the behavior you expect:
typeMakesSense=neverextendsnever ? 'yes' : 'no'// Resolves to 'yes'typeExtendsNever<T>=[T]extends[never] ? 'yes' : 'no'typeMakesSenseToo=ExtendsNever<{}>// Resolves to 'no'typeHuh=ExtendsNever<never>// is yes
Reacted by Ryan Cavanaugh, rickwebiii, Patrick Desjardins, Li Hongzhang, Alexei Zaviruha, Rohit Garg, Arthur Zahorski, Jeffrey Yasskin, Jerry Fan, Dan Vanderkam and 86 more
Reacted by eczn*, Anurag Hazra, csr632, someBrown, Yehor Kolesnykov, Lucas Genzelis, 銀桑, Sajeeb Ahamed, Oleksandr Koryttsev, Władysław Raczek and 10 more
Reacted by yossarian, Anurag Hazra, someBrown, 銀桑, Sajeeb Ahamed, Oleksandr Koryttsev, Władysław Raczek, Adam Misiorny, Changdae Park, Nick Partridge and 3 more
Why is never treated as an empty union but unknown isn’t treated as a “union of everything”? For symmetry I would expect T extends any ? ... where T = unknown to take both branches of the conditional type, the same way T = never takes neither.
I understand that any works this way but any isn’t really a “union of all types” in the usual sense - it also behaves as an “intersection of all types” when placed in a contravariant position so it’s kind of its own thing entirely and I expect it to act weird. 😉
I've got here because I was unable to understand why MakesSense and Huh are getting evaluated differently i.e. why both of them are not never. So probably here is a good place to post the answer:
According to the handbook chapter on Distributed Conditional Types
When conditional types act on a generic type, they become distributive when given a union type
It means that when Huh is evaluated, the never value of T is getting distributed and things are happening as Titian Cernicova-Dragomir (@dragomirtitian) explained. But evaluation of MakesSense doesn't include generics, so Conditional Type doesn't get distributed and so it acts as "expected" - never is getting compared with never
ZhanghaoH you can think of the type challenge problem not as "isUnion" but as "isDistributiveUnion" or "isUnionOfMoreThenOneElement". 1 is also a union - a union of 1 element. And never is a union - an empty union, or a union of 0 elements
Unions of 0 elements, 1 element, and 1+ elements behave differently in TS, and the problem is about detecting the third, non-singular case
Activity
dragomirtitian commentedon Jun 4, 2019
This is the expected behavior,
ExtendsNeveris a distributive conditional type. Conditional types distribute over unions. Basically ifTis a unionExtendsNeveris applied to each member of the union and the result is the union of all applications (ExtendsNever<'a' | 'b'> == ExtendsNever<'a' > | ExtendsNever<'b'>).neveris the empty union (ie a union with no members). This is hinted at by its behavior in a union'a' | never == 'a'. So when distributing overnever,ExtendsNeveris never applied, since there are no members in this union and thus the result is never.If you disable distribution for
ExtendsNever, you get the behavior you expect:rickwebiii commentedon Jun 4, 2019
Thanks for the clarification. I should really get around to reading my Homotopy Type Theory book.
fatcerberus commentedon Jun 5, 2019
Why is
nevertreated as an empty union butunknownisn’t treated as a “union of everything”? For symmetry I would expectT extends any ? ...whereT = unknownto take both branches of the conditional type, the same wayT = nevertakes neither.I understand that
anyworks this way butanyisn’t really a “union of all types” in the usual sense - it also behaves as an “intersection of all types” when placed in a contravariant position so it’s kind of its own thing entirely and I expect it to act weird. 😉jack-williams commentedon Aug 11, 2019
Bruce Pascoe (@fatcerberus); Discussed in #27418
Alexsey commentedon Jul 27, 2021
I've got here because I was unable to understand why
MakesSenseandHuhare getting evaluated differently i.e. why both of them are notnever. So probably here is a good place to post the answer:According to the handbook chapter on Distributed Conditional Types
It means that when
Huhis evaluated, thenevervalue ofTis getting distributed and things are happening as Titian Cernicova-Dragomir (@dragomirtitian) explained. But evaluation ofMakesSensedoesn't include generics, so Conditional Type doesn't get distributed and so it acts as "expected" -neveris getting compared withneverZhanghaoH commentedon Jul 19, 2022
but type-challenges-01097 ,its last test example thinks Never is not Union Type
Alexsey commentedon Jul 19, 2022
ZhanghaoH you can think of the type challenge problem not as "isUnion" but as "isDistributiveUnion" or "isUnionOfMoreThenOneElement".
1is also a union - a union of 1 element. Andneveris a union - an empty union, or a union of 0 elementsUnions of 0 elements, 1 element, and 1+ elements behave differently in TS, and the problem is about detecting the third, non-singular case
ZhanghaoH commentedon Jul 20, 2022
I see...thanks for your reply !