Skip to content
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

Type guards drop string index types from the narrowed type #15388

Closed
dherman opened this issue Apr 26, 2017 · 2 comments
Closed

Type guards drop string index types from the narrowed type #15388

dherman opened this issue Apr 26, 2017 · 2 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@dherman
Copy link

dherman commented Apr 26, 2017

I'm not sure if this is related to or even a duplicate of #14957. I was attempting to define an unknown type similar to the one described in #10715, with a predicate for testing the object case that narrows the type to a dictionary type, and ran into this issue. I'm including a more minified test case below.

I could easily believe that this isn't a bug but some misunderstanding on my part -- my apologies if so. It doesn't seem like it should be a problem with subtyping; AFAICT {[key: string]: number} is a subtype of {}, right?

TypeScript Version: 2.2.2 with all strict checks turned on

Code

type NumberDict = {[key: string]: number};
type NullableObject = {} | null;

function nullableObjectIsNumberDict(x: NullableObject): x is NumberDict {
    // bogus logic; the point of this example is just the types
    return !!x;
}

function use(x: NullableObject) {
    if (!nullableObjectIsNumberDict(x)) {
        return;
    }
    x // I expected NumberDict; TS says {}
}

Expected behavior:

The narrowed type of x should be NumberDict.

Actual behavior:

The narrowed type of x is {}.

@ahejlsberg
Copy link
Member

@dherman (Hi, Dave!) The issue you're running into here is that { } and { [key: string]: number } are both assignable to each other. The less obvious relation, that { } is assignable to { [key: string]: number }, technically should be true only when we know { } to exactly represent an empty object, but in practice that isn't feasible to track. Anyway, because { } is assignable to { [key: string]: number }, we take it to be a more specific type and therefore keep it following the type guard.

It works if you change your NullableObject declaration to

type NullableObject = object | null;

The object primitive type was introduced in 2.2. See #1809 for more context.

@ahejlsberg ahejlsberg added the Working as Intended The behavior described is the intended behavior; this is not a bug label Apr 26, 2017
@dherman
Copy link
Author

dherman commented Apr 27, 2017

Hi Anders! :) Thanks for the tip -- it's working great now: https://github.com/dherman/ts-unknown

@dherman dherman closed this as completed Apr 27, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

2 participants