Skip to content

Always assigning to an un-initialized variable in an exhaustive switch #30000

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
apexskier opened this issue Feb 20, 2019 · 3 comments · Fixed by #32695
Closed

Always assigning to an un-initialized variable in an exhaustive switch #30000

apexskier opened this issue Feb 20, 2019 · 3 comments · Fixed by #32695
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed Fixed A PR has been merged for this issue

Comments

@apexskier
Copy link

apexskier commented Feb 20, 2019

TypeScript Version: 3.4.0-dev.20190220

Search Terms:
switch case exhaustive assignment assign undefined TS2322

Code
Compile with the flags --noImplicitReturns --noImplicitAny --strictNullChecks

enum Test {
    A,
    B,
    C
}

// this example correctly understands that all possible values of t are handled
function foo(t: Test): number {
    switch (t) {
        case Test.A:
            return 1;
        case Test.B:
            return 2;
        case Test.C:
            return 3;
    }
}

// this should be identical to foo, since a is always assigned as the switch is exhaustive
function bar(t: Test): number {
    let a;
    switch (t) {
        case Test.A:
            a = 1;
        case Test.B:
            a = 2;
        case Test.C:
            a = 3;
    }
    return a; // I don't expect an error here
}

Expected behavior:
Code compiles with no errors. When a is returned in bar, its type is number. Typescript understands that an exhaustive switch with each case assigning to a type-inferred variable means that the variable's type is the union of each assigned value's type. I'd expect this to work because returns within an exhaustive switch are understood in a similar manner.

Actual behavior:
When a is returned in bar, its type is number | undefined

TS2322: Type 'number | undefined' is not assignable to type 'number'.
  Type 'undefined' is not assignable to type 'number'.

Playground Link:
link here
Turn on noImplicitReturns, noImplicitAny, and strictNullChecks.

Related Issues:

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Feb 20, 2019
@RyanCavanaugh RyanCavanaugh added Design Limitation Constraints of the existing architecture prevent this from being fixed and removed In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Apr 1, 2019
@RyanCavanaugh
Copy link
Member

Comments in #18362 still apply. We have some special-casing we're capable of doing for return, but it doesn't generalize to assignments.

@zpdDG4gta8XKpMCd
Copy link

zpdDG4gta8XKpMCd commented Apr 2, 2019

how is it not covered by officially recommended:

function noMore(_: never): never { throw new Error('Boom!'); }

// this should be identical to foo, since a is always assigned as the switch is exhaustive
function bar(t: Test): number {
  let a;
  switch (t) {
    case Test.A:
      a = 1;
      break;
    case Test.B:
      a = 2;
      break;
    case Test.C:
      a = 3;
      break;
    default: return noMore(t);
  }
  return a; // <-- works
}

?

@zpdDG4gta8XKpMCd
Copy link

image

http://www.typescriptlang.org/docs/handbook/advanced-types.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed Fixed A PR has been merged for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants