question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Discriminant property type guard not applied with bracket notation

See original GitHub issue

TypeScript Version: 2.0.0

Code

interface Square {
    kind: "square";
    size: number;
}

interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}

interface Circle {
    kind: "circle";
    radius: number;
}

type Shape = Square | Rectangle | Circle;

function area(s: Shape) {
    // In the following switch statement, the type of s is narrowed in each case clause
    // according to the value of the discriminant property, thus allowing the other properties
    // of that variant to be accessed without a type assertion.
    switch (s['kind']) {
        case "square": return s.size * s.size;
        case "rectangle": return s.width * s.height;
        case "circle": return Math.PI * s.radius * s.radius;
    }
}

Expected behavior:

The code compiles without errors.

Actual behavior:

sample.ts(24,33): error TS2339: Property 'size' does not exist on type 'Square | Rectangle | Circle'.
sample.ts(24,42): error TS2339: Property 'size' does not exist on type 'Square | Rectangle | Circle'.
sample.ts(25,36): error TS2339: Property 'width' does not exist on type 'Square | Rectangle | Circle'.
sample.ts(25,46): error TS2339: Property 'height' does not exist on type 'Square | Rectangle | Circle'.
sample.ts(26,43): error TS2339: Property 'radius' does not exist on type 'Square | Rectangle | Circle'.
sample.ts(26,54): error TS2339: Property 'radius' does not exist on type 'Square | Rectangle | Circle'.

Why this is bad:

I am trying to work with Dropbox’s new 2.0 SDK, which heavily uses tagged union types (especially for API errors). The discriminant property is named .tag, so it can only be accessed via bracket notation. I generated TypeScript typings for their new JavaScript SDK, and discovered this bug the hard way. 😦

Issue Analytics

  • State:open
  • Created 7 years ago
  • Reactions:52
  • Comments:30 (7 by maintainers)

github_iconTop GitHub Comments

14reactions
PurpleMagickcommented, Mar 25, 2020

I also found out that variables in bracket notation seem to not work in type guards. Very minimal reproduction:

const obj: { prop: string | null }  = { prop: "hello" };

if (typeof obj.prop === "string")
    obj.prop.length; //OK

if (typeof obj["prop"] === "string")
    obj["prop"].length; //OK

const key = "prop" as const;
if (typeof obj[key] === "string")
    obj[key].length; //error - object is possibly 'null'

Playground Link

It seems like using hardcoded values in bracket notation works, which is a fix made in https://github.com/microsoft/TypeScript/issues/28081 however, using a variable doesn’t offer the same behaviour, even if that variable is a const. So, it seems inconsistent. I’d understand if the variable was mutable or maybe even an object (perhaps its toString() doesn’t return the same thing every time) but for an immutable string variable, this should work the same as a hard-coded value.

13reactions
RyanCavanaughcommented, Aug 25, 2016

👍 Bracketed property access should work the same as dotted property access for the purposes of type guards when the indexed name matches a known property

Read more comments on GitHub >

github_iconTop Results From Across the Web

Typescript how to type guard promise type not empty if ...
to act as a discriminated union where the property value located at key 0 is used as the discriminant property.
Read more >
Documentation - TypeScript 4.4
If that type guard operates on a const , a readonly property, or an un-modified parameter, then TypeScript is able to narrow that...
Read more >
How to properly assign values to a dynamically constructed ...
Discriminant property type guard not applied with bracket notation #10530 · playground const extractShapeTypes: (shapes: ChordShape[]) => IShapeObj ...
Read more >
Type guards and assertion functions • Tackling TypeScript
Note that narrowing does not change the original type of value , it only ... If used to check for distinct properties, the...
Read more >
property 'usestaticassets' does not exist on type 'inestapplication'
microsoft/TypeScriptDiscriminant property type guard not applied with bracket notation#10530. Created about 6 years ago. 28. TypeScript Version: 2.0.0.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found