Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conditional types are evaluated too eagerly #34810

Closed
jvanbruegge opened this issue Oct 29, 2019 · 6 comments
Closed

Conditional types are evaluated too eagerly #34810

jvanbruegge opened this issue Oct 29, 2019 · 6 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@jvanbruegge
Copy link

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

@jvanbruegge
Copy link
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 added the Working as Intended The behavior described is the intended behavior; this is not a bug label Oct 29, 2019
@RyanCavanaugh
Copy link
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
Copy link
Author

So is it somehow possible to get the wanted behavior?

@RyanCavanaugh
Copy link
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
Copy link
Author

Sad. Ok, thank you

@rubenpieters
Copy link

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
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants