Skip to content

Conditional types are evaluated too eagerly #34810

Closed
@jvanbruegge

Description

@jvanbruegge

TypeScript Version: 3.7.x-dev.201xxxxx

Search Terms: conditional type eager, conditional type narrowing

Code

type FooHelper<X> = (arg1: X, arg2: X extends 0 ? number : string) => void;
type Foo = FooHelper<0 | 1>;

let x: number;
const foo: Foo = (t, d) => {
    if(t === 0) { x = d; }
}

Expected behavior:
No errors, t has type 0 | 1 in the function body which gets narrowed to just 0 in the body of the if statement. In the if statement, d should have type number, because we know that t has to have type 0.

Actual behavior:
Type 'string | number' is not assignable to type 'string'.

Playground Link: https://www.typescriptlang.org/play/?ts=3.7-Beta#code/C4TwDgpgBAYg9nAEhANpATgHgBoD4oC8UAFAIboDmAjAFxTYA0U5FATHdlBAB7AQB2AEwDOUAAxQA-FGHB0AS34UodfgFcAtgCMI6AJSF8ANzjzBAbgBQoSLASE7SVBkwSAPlCq4rllBGBQ3HSyCkpWAMZw-LJQAGYIdPBwDsTATIIGBPgA3pZQ+VDysamEBERiBtmBDhZQAL6WdUA

Related Issues: not really

Activity

jvanbruegge

jvanbruegge commented on Oct 29, 2019

@jvanbruegge
Author

I also tried to define Foo differently:

type Foo = ((arg1: 0, arg2: number) => void) & ((arg1: 1, arg2: string) => void);

But that infers t and d both as any

RyanCavanaugh

RyanCavanaugh commented on Oct 29, 2019

@RyanCavanaugh
Member

Foo isn't a generic type, so the conditional has already been evaluated. Conditional types only stay unresolved when they're in a generic type.

jvanbruegge

jvanbruegge commented on Oct 29, 2019

@jvanbruegge
Author

So is it somehow possible to get the wanted behavior?

RyanCavanaugh

RyanCavanaugh commented on Oct 29, 2019

@RyanCavanaugh
Member

Not really, no. You'd need #33014 at an absolute minimum - there's currently no mechanism that would allow for the kind of narrowing implied here.

jvanbruegge

jvanbruegge commented on Oct 29, 2019

@jvanbruegge
Author

Sad. Ok, thank you

rubenpieters

rubenpieters commented on Nov 2, 2019

@rubenpieters

If I understand correctly, your intention is kind of like the following: the parameter arg2 is dependent on the type of arg1. Actually, it is dependent on the value of arg1, but that is inexpressible.

let x: number;

type F = { 0: number, 1: string }

function foo<X extends 0 | 1>(arg1: X, arg2: F[X]): void {
  if (arg1 === 0) {
    x = arg2;
  }
}

Unfortunately, just #33014 won't solve your problem (it is similar to the rejected case a here). Since the following can happen:

foo(0 as 0 | 1, "oops");

Meaning: the value of arg1 can be 0, while the type variable X can be instantiated with 0 | 1, which means that arg2 can actually hold a string. As a result the logic of narrowing X to 0 based on the check arg1 === 0 is unsound.

We would additionally require something like #30284 to solve this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @jvanbruegge@rubenpieters@RyanCavanaugh

        Issue actions

          Conditional types are evaluated too eagerly · Issue #34810 · microsoft/TypeScript