Skip to content

Computed generic argument type is not narrowed #44358

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

Closed
qwelias opened this issue May 31, 2021 · 5 comments
Closed

Computed generic argument type is not narrowed #44358

qwelias opened this issue May 31, 2021 · 5 comments

Comments

@qwelias
Copy link

qwelias commented May 31, 2021

Bug Report

πŸ”Ž Search Terms

computed generic function argument guard narrow

πŸ•— Version & Regression Information

v4.3.2

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about generics

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

type Select<T extends 'a' | 'b'> = {
    a: string;
    b: number;
}[T];

const select = <T extends 'a' | 'b'>(type: T, data: Select<T>) => {
    if (type === 'b') return data * 5; // The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.(2362)
    return 1;
}

πŸ™ Actual behavior

Cannot data * 5 because The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.(2362)

πŸ™‚ Expected behavior

data * 5 is valid because it's within the code branch where type === 'b' and type of data in that case is number

@qwelias
Copy link
Author

qwelias commented May 31, 2021

There's a workaround to use tuple type for arguments

type SelectArgs =
    | ['a', string]
    | ['b', number]

const select = (...args: SelectArgs) => {
    if (args[0] === 'b') return args[1] * 5;
    return 1;
}

But in that case one cannot destructure them, because then it would break for some reason, so have to keep using indexes :(
I also tried to overload function signature, but it had similar issue IIRC

@MartinJohns
Copy link
Contributor

MartinJohns commented May 31, 2021

Because such an operation would be unsound:

select<'a' | 'b'>('b', 'hello')

You would need #27808 first.


You have the very common misconception that T extends 'a' | 'b' means T can either be 'a' or 'b'. But T can be 'a' | 'b' as well.

@qwelias
Copy link
Author

qwelias commented May 31, 2021

So is the only way right now to do something like this is to use tuples like in #44358 (comment) (or analogous object type) without the ability to destructure?
ts pg

@MartinJohns
Copy link
Contributor

Yes. Destructure would require #12184 / #44169.

@qwelias
Copy link
Author

qwelias commented Jun 1, 2021

Thanks for links, looks like it depends on existing issues.

@qwelias qwelias closed this as completed Jun 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants