microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
99.74k stars 12.35k forks source link

Intersection will be splited when wrap a tuple and use `extends` tuple. #56811

Closed NWYLZW closed 8 months ago

NWYLZW commented 8 months ago

🔎 Search Terms

🕗 Version & Regression Information

⏯ Playground Link

https://www.typescriptlang.org/play?#code/KYDwDg9gTgLgBDAnmYcDCEA2ngGMYAMAPACoA0cAqgHxwC8cJcoMwAdgCYDOcA2gLpwA-HDbAAbsChwAXHyYAyKvwBQoSLATJUGbHhgBGUhRr15cJZVUqkKRgTMAKA3AA+cAExu4AcgDMPgCUFqIArgC2AEZSKgD0sXCJAHpCNtqMLgy6OPjELu5e7n7ePgAsPhRsEdFQ1HEJyam2qCReWVg5hkT5nt7F7mUVYVFSdfGJcCkqQA

💻 Code

export type Collect0<T, U> = T extends [] ? never : [T & U]
export type Collect1<T, U> = [T & U]

type T0 = (1 | 2 | '3') & number
//   ^? type T0 = 1 | 2
type T1 = Collect0<1 | 2 | 3 | '4', number>
//   ^? type T1 = [never] | [1] | [2] | [3]
type T2 = Collect1<1 | 2 | 3 | '4', number>
//   ^? type T2 = [1 | 2 | 3]

🙁 Actual behavior

type T1 = Collect0<1 | 2 | 3 | '4', number>
//   ^? type T1 = [never] | [1] | [2] | [3]

🙂 Expected behavior

type T1 = Collect0<1 | 2 | 3 | '4', number>
//   ^? type T1 = [1 | 2 | 3]

Additional information about the issue

No response

fatcerberus commented 8 months ago

Working as intended. https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types

fatcerberus commented 8 months ago

On a side note, T0 should be written as

type T0 = (1 | 2 | '3') & number

because & has higher precedence than |.

NWYLZW commented 8 months ago

Working as intended. https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types

Yes, I've seen this. So is this true even in the else branch? Here, this looks very strange.

fatcerberus commented 8 months ago

So is this true even in the else branch?

Yes. Distribution is over the entire conditional type, not individual branches. In other words it runs the ternary conditional expression once for each member of the union and then reassembles it at the end.

This does what you want:

export type Collect0<T, U> = [T] extends [[]] ? never : [T & U]
NWYLZW commented 8 months ago
image

You are faster than me, haha.

NWYLZW commented 8 months ago

A little weird, but I get it, thanks for your help.