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

Type narrowing of string literal unions #9314

Closed
gcnew opened this issue Jun 22, 2016 · 4 comments
Closed

Type narrowing of string literal unions #9314

gcnew opened this issue Jun 22, 2016 · 4 comments
Labels
Duplicate An existing issue was already created

Comments

@gcnew
Copy link
Contributor

gcnew commented Jun 22, 2016

TypeScript Version:

nightly (1.9.0-dev.20160217) + strictNullChecks

Code

type Easing = 'ease-in' | 'ease-out' | 'ease-in-out';

function getEasingFunction(easing: Easing): (val: number) => number {
    switch (easing) {
        case 'ease-in':     return t => t * t;
        case 'ease-out':    return t => t * (2-t);
        case 'ease-in-out': return t => t<0.5 ? (2*t*t) : -1+(4-2*t)*t;
    }
    // `easing` should be of type `never` here
    // however the actual type is `'ease-in' | 'ease-out' | 'ease-in-out'`
}

Expected behavior:
After an exhaustive switch the switched variable should be of type never as per #9163. Conversely, if the switch is not exhaustive and the return type does not accomodate for undefined, an error should be issued.

Actual behavior:
String literal union types are not narrowed thus an error is always issued.

@gcnew
Copy link
Contributor Author

gcnew commented Jun 22, 2016

PS: Kudos for #9163 and all the work you've put in TS. It's a trully amazing language.

@aluanhaddad
Copy link
Contributor

@gcnew interestingly, this works:

type Easing = { val: 'ease-in' } | { val: 'ease-out' } | { val: 'ease-in-out' };

function getEasingFunction(easing: Easing): (val: number) => number {
    switch (easing.val) {
        case 'ease-in': return t => t * t;
        case 'ease-out': return t => t * (2 - t);
        case 'ease-in-out': return t => t < 0.5 ? (2 * t * t) : -1 + (4 - 2 * t) * t;
    }
    // `easing` should be of type `never` here
    // however the actual type is `'ease-in' | 'ease-out' | 'ease-in-out'`
}

@gcnew
Copy link
Contributor Author

gcnew commented Jun 22, 2016

@aluanhaddad Yeah, another workaround that doesn't change Easing alias:

type Easing = 'ease-in' | 'ease-out' | 'ease-in-out';

function assertNever(x: never): never {
    throw `Not a never ${ x }`;
}

function getEasingFunction(easing: Easing): (val: number) => number {
    const x = { test: easing };
    switch (x.test) {
        case 'ease-in':     return t => t * t;
        case 'ease-out':    return t => t * (2-t);
        case 'ease-in-out': return t => t<0.5 ? (2*t*t) : -1+(4-2*t)*t;

        default:
            return assertNever(x);
    }
}

@ahejlsberg
Copy link
Member

This is a duplicate of #7447.

@ahejlsberg ahejlsberg added the Duplicate An existing issue was already created label Jun 22, 2016
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants