Skip to content

Type guard using square bracket notation does not narrow type #28081

Closed

Description

@mjdease

TypeScript Version: 3.2.0-dev.20181023

Search Terms:
type guard square bracket notation narrowing

Code

interface ConfigType {
    [key: string]: boolean | { prop: string };
}

const config: ConfigType = {
    works: { prop: 'foo' },
    'works-not': { prop: 'bar' },
    bool: true,
};

if (typeof config.works !== 'boolean') {
    config.works.prop = 'test';
}

if (typeof config['works-not'] !== 'boolean') {
    config['works-not'].prop = 'test'; // [ts] Property 'prop' does not exist on type 'false'.
}

Expected behavior:
The type guard using square bracket notation narrows the type in the same way the type guard using dot notation does, resulting in no type errors in the above example.

Actual behavior:
The type guard specifically using square bracket notation does not narrow the type.

Playground Link:
Link

Related Issues:
#10530

Activity

  1. DanielRosenwasser commented on Oct 24, 2018

    @DanielRosenwasser
    Member
  2. sandersn commented on Oct 24, 2018

    @sandersn
    Member

    No, this is not a duplicate. It's a missed interaction between indexed access and index signatures, where property access + index signatures works.
    Here's a smaller repro, without the dashed-name red herring:

    interface ConfigType {
        [key: string]: boolean | { prop: string };
    }
    
    const config: ConfigType = {
        works: { prop: 'foo' }
    };
    
    if (typeof config.works !== 'boolean') {
        config['works'].prop = 'test'; // error, config['works']: boolean | { 'prop': string }
        config.works.prop = 'test'; // ok
    }

    The behaviour is the same if the discriminator is typeof config['works'] !== 'boolean', so I think it's just a failure of control flow from indexed access in this case.

    Also note that if we have interface ConfigType { works: boolean | { prop: string } } then both statements work.

  3. rkirov commented on Feb 25, 2019

    @rkirov
    Contributor

    Just a drive by comment. We are working in Google internal TS to forbid using the prop access on index signature objects. The main reason is that to Closure prop access means safe to rename and index access means don't touch. This optimization pass is done without global knowledge (other than whats' coming externally) so its up to the developer to decide whether it is really "safe" to rename.

    The transformation from foo.bar to foo['bar'] is mostly noop TS-wise, except for this issue.

  4. sandersn commented on May 20, 2019

    @sandersn
    Member

    Turns out this was just missing. It is super simple to add since it's the same code as property access. I just need to make sure performance is OK.

  5. sandersn commented on Jun 14, 2019

    @sandersn
    Member

    Update: The PR is trivial and performance is, as expected, bad for probably-uncommon adversarial code. We need to discuss in the design meeting whether to take this change.

  6. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugA bug in TypeScriptDomain: check: Control FlowThe issue relates to control flow analysisFix AvailableA PR has been opened for this issueFixedA PR has been merged for this issue

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions