Skip to content

narrowing doesn't work for literal strings in switch when united with unconstrained string #10417

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
zpdDG4gta8XKpMCd opened this issue Aug 18, 2016 · 5 comments
Labels
Fixed A PR has been merged for this issue Suggestion An idea for TypeScript

Comments

@zpdDG4gta8XKpMCd
Copy link

zpdDG4gta8XKpMCd commented Aug 18, 2016

nightly build, August 18, 2016

function useA(value: 'a'): void { }
function useB(value: 'b'): void { }
function useSomethingElse(value: string): void { }
declare var kind: string | 'a' | 'b';
switch (kind) {
    case 'a': useA(kind); break; // expected kind to be 'a', actual 'a' | string
    case 'b': useB(kind); break;
    default: useSomethingElse(kind); break;
}
@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Aug 18, 2016
@RyanCavanaugh
Copy link
Member

Side question: what was your intent declaring something string | 'a' | 'b' ?

@zpdDG4gta8XKpMCd
Copy link
Author

zpdDG4gta8XKpMCd commented Aug 18, 2016

example1: DB guys introduced a new possible value to a set of well-known values, without letting anyone know, this new case has to be treated separately as a failure/default case/you name it

example2: this one is lengthy, gradual open-ended narrowing via composing resolving functions

SomeStuff is a union of One and Another why not to take advantage of their resolver functions

type One = 'a' | 'b';
interface ViaOne<r> {
   caseOfA: (value: 'a') => r;
   caseOfB: (value: 'b') => r;
}
function viaOne<r>(kind: One | string, via: ViaOne<r>) : Optional<r> {
   switch (kind) {
       case 'a': return someFrom(via.caseOfA(kind));
       case 'b': return someFrom(via.caseOfB(kind));
       default: return none;
   }
}
type Another = 'c' | 'd';
interface ViaAnother<r> {
    caseOfC: (value: 'c') => r;
    caseOfD: (value: 'd') => r;
}
function viaAnother<r>(kind: Another | string, via: ViaAnother<r>): Optional<r> {
   switch (kind) {
       case 'c': return someFrom(via.caseOfA(kind));
       case 'd': return someFrom(via.caseOfB(kind));
       default: return none;
   }
}

type CrazyStuff = One | Another;
interface ViaCrazyStuff<r> extends ViaOne<r>, ViaAnother<r> {}

function viaCrazyStuff<r>(value: CrazyStuff, via: ViaCrazyStuff<r>): Optional<r> {
   const tryOne = viaOne(value, via);
   if (isSome(tryOne)) {
       return tryOne;
   } else {
       return viaAnother(value, via);
   }
}

@ahejlsberg
Copy link
Member

This is working as intended, so I'm changing the label to a suggestion. We currently only narrow in a switch statement when the type of the switch expression is a union of unit types. In your example, there's really no difference between the type string and string | 'a' | 'b'.

@ahejlsberg ahejlsberg added Suggestion An idea for TypeScript and removed Bug A bug in TypeScript labels Aug 18, 2016
@zpdDG4gta8XKpMCd
Copy link
Author

zpdDG4gta8XKpMCd commented Aug 19, 2016

fyi, there is a workaround using T extends string rather than plain string

function useA(value: 'a'): void { }
function useB(value: 'b'): void { }
function useSomethingElse(value: string): void { }

function test<c extends string>(kind: 'a' | 'b' | c) {
    switch (kind) {
        case 'a': useA(kind); break;
        case 'b': useB(kind); break;
        default: useSomethingElse(kind);
    }
}

@RyanCavanaugh RyanCavanaugh added the In Discussion Not yet reached consensus label Aug 19, 2016
@RyanCavanaugh
Copy link
Member

@ahejlsberg why wouldn't we narrow whenever a literal value matches a literal in a union?

@mhegazy mhegazy added Fixed A PR has been merged for this issue and removed In Discussion Not yet reached consensus labels Oct 14, 2016
@mhegazy mhegazy added this to the TypeScript 2.1 milestone Oct 14, 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
Fixed A PR has been merged for this issue Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants