#Distributivity & union of results

19 messages · Page 1 of 1 (latest)

spark bay
#

Hi,

Simple question:
Can somebody explain to me why test resolves to no?
Shouldn't it be no | yes instead?

type test = 'k' | [1] extends unknown[] ? 'yes' : 'no';
//   ^? type test = "no"
next sapphire
#

you can read extends as "is assignable to". in that light, it's 'no' for the same reason that this is a type error:

edgy basinBOT
#
const a: 'k' | [1] = 'k'
const b: unknown[] = a
//    ^
// Type 'string' is not assignable to type 'unknown[]'.
next sapphire
#

in case operator precedence was the source of your confusion, what you wrote is equivalent to this:

edgy basinBOT
#
type test = ('k' | [1]) extends unknown[] ? 'yes' : 'no';
//   ^? - type test = "no"
next sapphire
#

not this:

edgy basinBOT
#
type test = 'k' | ([1] extends unknown[] ? 'yes' : 'no');
//   ^? - type test = "k" | "yes"
spark bay
#

I really don't get it. Why does distributivity fail here?

next sapphire
#

can you rephrase the question? distributivity of what, exactly?

#

were you expecting 'yes' | 'no' because each member of the union is checked individually?

#

if so, the answer is that conditional types are only distributive when the LHS is a bare type parameter

#

for example:

edgy basinBOT
#
type test2<T> = T extends unknown[] ? 'yes' : 'no';
type test3 = test2<'a' | [1]>
//   ^? - type test3 = "no" | "yes"
spark bay
#

right exactly - that's what I was looking for... carry on plz...

#

so in other words distributivity fails unless I use generics, right?

next sapphire
#

i wouldn't say it "fails". it's only designed to work when operating on a type parameter

#

you can use infer to create a type parameter too. i'll show an example in a moment

edgy basinBOT
#
type test4 = 'a' | [1] extends infer T ? T extends unknown[] ? 'yes' : 'no' : never
//   ^? - type test4 = "no" | "yes"
spark bay
#

That's perfect thank you! Exactly the information I was looking for 🙏