Skip to content

Switch statements should discriminate by class prototype #38373

@karol-majewski

Description

@karol-majewski

TypeScript Version: Nightly (4.0.0-dev.20200505)

Search Terms:

  • switch
  • control flow
  • discriminated union
  • untagged union
  • disjoin
  • prototype
  • constructor
  • 2339

Code

class Failure {}

class Success {
    constructor(readonly value: string) { }
}

function main(result: Failure | Success): void {
    switch (result.constructor) {
        case Success:
            console.log(`Success! Your value is ${result.value}`); // Property 'value' does not exist on type 'Failure | Success'.
            break;
        case Failure:
            console.log('Oh noes');
    }
}

main(new Success("🍆"));
Output
"use strict";
class Failure {
}
class Success {
    constructor(value) {
        this.value = value;
    }
}
function main(result) {
    switch (result.constructor) {
        case Success:
            console.log(`Success! Your value is ${result.value}`); // Property 'value' does not exist on type 'Failure | Success'.
            break;
        case Failure:
            console.log('Oh noes');
    }
}
main(new Success("🍆"));
Compiler Options
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "useDefineForClassFields": false,
    "alwaysStrict": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "downlevelIteration": false,
    "noEmitHelpers": false,
    "noLib": false,
    "noStrictGenericChecks": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "esModuleInterop": true,
    "preserveConstEnums": false,
    "removeComments": false,
    "skipLibCheck": false,
    "checkJs": false,
    "allowJs": false,
    "declaration": true,
    "experimentalDecorators": false,
    "emitDecoratorMetadata": false,
    "target": "ES2017",
    "module": "ESNext"
  }
}

Playground Link: Provided

Expected behavior:

No compile-time error. result.value should be of type string. That's what is happening in runtime.

Actual behavior:

We get a type error.

Property 'value' does not exist on type 'Failure | Success'. (2339)

Note that it works if you use if statements.

class Failure {}

class Success {
    constructor(readonly value: string) { }
}

function main(result: Failure | Success): void {
    if (result instanceof Success) {
        console.log(`Success! Your value is ${result.value}`);
    }

    if (result instanceof Failure) {
        console.log('Oh noes');
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions