Typescript: Simpler Expression of Complex Type Conditions

Created on 15 Oct 2020  Â·  3Comments  Â·  Source: microsoft/TypeScript

Search Terms

Multiple, type, condition, expression, no, extra, case, and, or

Suggestion

I often find myself repeating sub-branches of conditional types, which makes reasoning about control flow more difficult. I'd recommend the same logical operators to those of JS land, at the type-level.

Example

Let's say we have three boolean types––ConditionA, ConditionB and ConditionC––and we want to gather the number of true conditions in a new type Result, which will evaluate to 0 | 1 | 2 | 3 depending on the aforementioned conditions. The runtime equivalent can be expressed quite legibly:

const Result =
  (conditionA && conditionB && conditionC)
    ? 3
    : (
        (conditionA && conditionB) ||
        (conditionB && conditionC) ||
        (conditionA && conditionC)
      )
      ? 2
      : (conditionA || conditionB || conditionC)
        ? 1
        : 0;

The type-level equivalent is not as obvious in its meaning, and we end up repeating the potential results.

type Result =
  ConditionA extends true
    ? ConditionB extends true
      ? ConditionC extends true
        ? 3
        : 2
      : ConditionC extends true
        ? 2
        : 1
    : ConditionB extends true
      ? ConditionC extends true
        ? 2
        : 1
      : ConditionC extends true
        ? 1
        : 0;

While it nets more code, the following is––imo––easier to understand.

type Result =
  (ConditionA extends true && ConditionB extends true && ConditionC extends true)
    ? 3
    : (
        (ConditionA extends true && ConditionB extends true) ||
        (ConditionB extends true && ConditionC extends true) ||
        (ConditionA extends true && ConditionC extends true)
      )
      ? 2
      : (ConditionA extends true || ConditionB extends true || ConditionC extends true)
        ? 1
        : 0;

Ideally, the same rules from JS-land could apply to judging whether a type is a subtype of truthy, so we could do this:

type Result =
  (ConditionA && ConditionB && ConditionC)
    ? 3
    : (
        (ConditionA && ConditionB) ||
        (ConditionB && ConditionC) ||
        (ConditionA && ConditionC)
      )
      ? 2
      : (ConditionA || ConditionB || ConditionC)
        ? 1
        : 0;

Checklist

My suggestion meets these guidelines:

  • [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • [x] This wouldn't change the runtime behavior of existing JavaScript code
  • [x] This could be implemented without emitting different JS based on the types of the expressions
  • [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • [x] This feature would agree with the rest of TypeScript's Design Goals.
Awaiting More Feedback Suggestion

Most helpful comment

Also, for conditions consisting solely of &&s, infer Ts should be allowed, and be processed left to right, i.e.:

type T = (X extends [infer X0, infer X1] && Y extends [X0, infer Y1]) ? [X0, [X1, Y1]] : never

Should be valid.

All 3 comments

+1

Additionally, I think it should allow the below, for parity with intersection / union and also cleaner multiline conditionals

type Result =
  (ConditionA && ConditionB && ConditionC)
    ? 3
    : (
        || (ConditionA && ConditionB)
        || (ConditionB && ConditionC)
        || (ConditionA && ConditionC)
      )
      ? 2
      : (ConditionA || ConditionB || ConditionC)
        ? 1
        : 0;

I really like this for when I have long chains of A ? B ? C ? T : never : never : never; (A && B && C) ? T : never seems a lot cleaner.

I don't particularly care for requiring parens, but if it's necessary for avoiding ambiguity, it's still better than what we have right now.

Also, for conditions consisting solely of &&s, infer Ts should be allowed, and be processed left to right, i.e.:

type T = (X extends [infer X0, infer X1] && Y extends [X0, infer Y1]) ? [X0, [X1, Y1]] : never

Should be valid.

@tjjfvi absolutely! Great idea.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

uber5001 picture uber5001  Â·  3Comments

MartynasZilinskas picture MartynasZilinskas  Â·  3Comments

blendsdk picture blendsdk  Â·  3Comments

dlaberge picture dlaberge  Â·  3Comments

jbondc picture jbondc  Â·  3Comments