Skip to content

Detect symbol-named properties as union discriminants #36463

@Zemnmez

Description

@Zemnmez

TypeScript Version: 3.7.5

Search Terms:
discriminated union symbol, AsyncIterator, Iterator.

Expected behavior:
Unions discriminated by symbol members should be narrowable.

Actual behavior:
No narrowing occurs.

Code

type m = {
    [Symbol.unscopables](): undefined
    cool:1
}

type b = {
    [Symbol.iterator](): undefined
    [Symbol.unscopables]():2
}

declare const k: m | b
declare function f(v: b): void


/*
Element implicitly has an 'any' type because expression of type
'symbol' can't be used to index type 'm | b'.(7053)
*/
if (k[Symbol.iterator] == undefined) f(k);


type m2 = {
  [Symbol.iterator](): 1
}

type b2 = {
  [Symbol.unscopables](): 2
}

declare function f2(b: b2): void;
declare const k2: m2 | b2;

/*
Argument of type 'm2 | b2' is not assignable to parameter of type 'b2'.
  Property '[Symbol.unscopables]' is missing in type 'm2' but required in type 'b2'.(2345)
input.ts(27, 3): '[Symbol.unscopables]' is declared here.
*/
if (Symbol.unscopables in k2) f2(k2)
Output
"use strict";
/*
Element implicitly has an 'any' type because expression of type
'symbol' can't be used to index type 'm | b'.(7053)
*/
if (k[Symbol.iterator] == undefined)
    f(k);
/*
Argument of type 'm2 | b2' is not assignable to parameter of type 'b2'.
  Property '[Symbol.unscopables]' is missing in type 'm2' but required in type 'b2'.(2345)
input.ts(27, 3): '[Symbol.unscopables]' is declared here.
*/
if (Symbol.unscopables in k2)
    f2(k2);
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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScript

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions