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

if(value!=null) doesn't narrow type in some situations #31050

Closed
mysticatea opened this issue Apr 21, 2019 · 7 comments
Closed

if(value!=null) doesn't narrow type in some situations #31050

mysticatea opened this issue Apr 21, 2019 · 7 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@mysticatea
Copy link

TypeScript Version: 3.5.0-dev.20190420

Search Terms: narrowing strictNullChecks

Code

type Box<T extends string> = { [key in T]: number | null }

function test1<T extends string>(key: T, { [key]: value }: Box<T>): number {
    if (value != null) {
        return value //ERROR: Type 'null' is not assignable to type 'number'.
    }
    return 0
}

function test2<T extends string>(key: T, box: Box<T>): number {
    if (box[key] != null) {
        return box[key] //ERROR: Type 'null' is not assignable to type 'number'.
    }
    return 0
}

Expected behavior:

In the block of if (value != null) {...}, the value is considered as non-null.

Actual behavior:

In the block of if (value != null) {...}, the value is considered as nullable.

Playground Link: https://www.typescriptlang.org/play/index.html#src=type%20Box%3CT%20extends%20string%3E%20%3D%20%7B%20%5Bkey%20in%20T%5D%3A%20number%20%7C%20null%20%7D%0D%0A%0D%0Afunction%20test1%3CT%20extends%20string%3E(key%3A%20T%2C%20%7B%20%5Bkey%5D%3A%20value%20%7D%3A%20Box%3CT%3E)%3A%20number%20%7B%0D%0A%20%20%20%20if%20(value%20!%3D%20null)%20%7B%0D%0A%20%20%20%20%20%20%20%20return%20value%20%2F%2FERROR%3A%20Type%20'null'%20is%20not%20assignable%20to%20type%20'number'.%0D%0A%20%20%20%20%7D%0D%0A%20%20%20%20return%200%0D%0A%7D%0D%0A%0D%0Afunction%20test2%3CT%20extends%20string%3E(key%3A%20T%2C%20box%3A%20Box%3CT%3E)%3A%20number%20%7B%0D%0A%20%20%20%20if%20(box%5Bkey%5D%20!%3D%20null)%20%7B%0D%0A%20%20%20%20%20%20%20%20return%20box%5Bkey%5D%20%2F%2FERROR%3A%20Type%20'null'%20is%20not%20assignable%20to%20type%20'number'.%0D%0A%20%20%20%20%7D%0D%0A%20%20%20%20return%200%0D%0A%7D%0D%0A

(please turn strictNullChecks option on.)

Related Issues:

Maybe #22137 is related.

@jack-williams
Copy link
Collaborator

I think this is a current design limitation (and the lack of negation types). To add a negative constraint TypeScript would need to convert value to its union constraint and then filter the resulting union. This would work for your example, but is not a general fix because you lose the generic-ness of the type which might be needed.

There may be some argument to say that a conditional !== null should narrow using NonNullable instead, but this means adding another special case. I think once negation types get merged there may be movements towards changing the narrowing mechanics to support this in a uniform way. See: #22348.

@mysticatea
Copy link
Author

I'm not sure if this is related to negation types. How this issue case is different from the following case?

function test3(value: number | null): number {
    if (value != null) {
        return value
    }
    return 0
}

@ahejlsberg
Copy link
Member

This is working as intended. We don't narrow types for array elements with computed indices (computed here meaning any expression that isn't just a literal). In the example above, box[key] is considered to have a computed index. We only narrow when the value is copied into a local variable, as is the case with the destructuring.

@ahejlsberg ahejlsberg added the Working as Intended The behavior described is the intended behavior; this is not a bug label Apr 22, 2019
@mysticatea
Copy link
Author

We only narrow when the value is copied into a local variable

@ahejlsberg Hi. That value is a local variable.

type Box<T extends string> = { [key in T]: number | null }

function test1<T extends string>(key: T, { [key]: value }: Box<T>): number {
    if (value != null) {
        return value //ERROR: Type 'null' is not assignable to type 'number'.
    }
    return 0
}

@RyanCavanaugh
Copy link
Member

@mysticatea generic values like that can't be narrowed against null/undefined yet. See #22348

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@haoadoreorange
Copy link

haoadoreorange commented Jun 2, 2021

This is working as intended. We don't narrow types for array elements with computed indices (computed here meaning any expression that isn't just a literal). In the example above, box[key] is considered to have a computed index. We only narrow when the value is copied into a local variable, as is the case with the destructuring.

Why is it the case ? Because of possibility of async ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
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

6 participants