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

Problems with inference in unions of tuples #21949

Closed
saabi opened this issue Feb 14, 2018 · 6 comments
Closed

Problems with inference in unions of tuples #21949

saabi opened this issue Feb 14, 2018 · 6 comments
Labels
Duplicate An existing issue was already created

Comments

@saabi
Copy link

saabi commented Feb 14, 2018

Search Terms: tuples unions inference

In the following example, for constant f, the inferred type should be number | undefined (or even narrowed down to undefined in this particularly obvious case) but instead it's being incorrectly inferred as string.

I tried this on 2.8.0-dev.20180214 as well and it's still showing the same behavior.

type a = [string] | [string, number]

const b: a = ['bye', 1];    // accepted by the compiler
const c: a = ['hi'];        // accepted by the compiler
const d: a = ['hi', 'bye']  // correctly throws an error

const e = b[1];  // inferred type of e is number
const f = c[1];  // inferred type of f is string (!?) should be: number | undefined

Here's a playground link

@saabi
Copy link
Author

saabi commented Feb 14, 2018

Ok, after investigating the playground example a little bit more I get what's going on.

Type narrowing is being too aggressive (to my expectations anyway) and the compiler is narrowing c down to a simple string[]. The rest of the behavior is to be expected after that.

This is not an implementation bug, but I think it's arguable whether it's a design bug, as it kind of makes futile the intent of having the tuple union in the first place. It probably depends on what that intention is, but there is no way of specifying that in the definition.

The only fix (... I can think of so far...) is to cast at every access, which is kind of annoying. If I have to cast everywhere I use such a variable it's like not casting anywhere since the possibility of introducing a casting error somewhere defeats the purpose of typing.

If narrowing is unavoidable, then it should probably narrow to '[string]' instead of 'string[]'

@RyanCavanaugh
Copy link
Member

Basically a duplicate of #5203. We know (from analyzing the code) that c is [string] (you can see this by writing ['hi'] as any instead). But if you try to OOB a tuple we just defer to a union of its element types because, well, we do (I don't have a good defense of this behavior).

@jcalz
Copy link
Contributor

jcalz commented Feb 14, 2018

Depending on your use case, you could change a to be

type a = {0: string, 1?: never, length: 1}  | {0: string, 1: number, length: 2}

or

type a = ([string] & {1?: never})| [string, number]

@saabi
Copy link
Author

saabi commented Feb 14, 2018

Continuing with my argument from my last comment:

If narrowing is unavoidable, then it should probably narrow to '[string]' instead of 'string[]'

I tried it on the playground

type g = [string];

const h = ["I'm back!"];    // inferred as string[]
const i: g = ["I'm back!"];

const j = h[1]; // j is inferred as string. Correct
const k = i[1]; // k is inferred as string. (mmmh...?)
// Shouldn't it be undefined after typescript 2.7?

@RyanCavanaugh I just read your comments on #5203 but I must say, shouldn't k definitely be undefined after fixed length tuples were introduced in typescript 2.7?

@saabi
Copy link
Author

saabi commented Feb 14, 2018

@jcalz You're right, that would work.

But after investigating on the playground a bit more (see my previous comments) and after the introduction of fixed length tuples in 2.7, I think there might actually be an implementation bug (not the one imagined in the OP)

@mhegazy mhegazy added the Duplicate An existing issue was already created label Jul 16, 2018
@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

5 participants