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

Wrong narrowing with the in operator #53910

Closed
AxelHaddad opened this issue Apr 19, 2023 · 5 comments
Closed

Wrong narrowing with the in operator #53910

AxelHaddad opened this issue Apr 19, 2023 · 5 comments
Labels
Duplicate An existing issue was already created

Comments

@AxelHaddad
Copy link

AxelHaddad commented Apr 19, 2023

Bug Report

It seems that the built-in narrowing via the in operator can lead to narrowing mistakes (false positive).

🔎 Search Terms

Narrowing, in, subtypes, type guards

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about type guards/narrowing

⏯ Playground Link

  • a first Playground leading to false narrowing
  • A second Playground leading to uncatched type errors that breaks at runtime.

💻 Code

type PartialType = {foo: string}
type FullType = {foo:string, bar:string, baz: string}

function isFullType(object: FullType | PartialType): object is FullType {
    if (!("bar" in object)) return false;
    const _:FullType = object; // Just to witness that Typescript thinks object is FullType
    return true;
}

🙁 Actual behavior

No typescript error.

🙂 Expected behavior

Typescript should not think that object is of type FullType as for instance all those three following objects, compatible with PartialType, are clearly not FullType:

{ foo: "hello", bar: "world" }
{ foo: "hello", bar: undefined, baz: 24 }
{ foo: "hello", bar: 42 }

Therefore it should raise an error at the line const _:FullType = object;.

@AxelHaddad AxelHaddad changed the title Wrong narrowing with the in keyword Wrong narrowing with the in operator Apr 19, 2023
@MartinJohns
Copy link
Contributor

Duplicate of #34975. The in operator is intentionally unsound.

@ziloen
Copy link

ziloen commented Apr 19, 2023

Maybe you should use type PartialType = { foo: string } & { [K in string]: unknown }

@AxelHaddad
Copy link
Author

AxelHaddad commented Apr 19, 2023

Ok, I understand that if I were to use the in keyword, I accept that Typescript won't be sound anymore.

That being said, how can I , without using in, correctly narrow an object obj of type PartialType = {foo: string} to the type FullType = {foo:string, bar:string, baz: string} since I cannot call obj.bar or obj["bar"] without asserting "bar" in obj first?

Using PartialType & { [K in string]: unknown } does not work...

type PartialType = {foo: string}
type FullType = {foo:string, bar:string, baz: string}


function isFullType(object: FullType | PartialType): object is FullType {
    type CorrectPartialType = PartialType  & { [K in string]: unknown }
    const obj: CorrectPartialType | FullType = object
    if (typeof(obj.bar) == 'string' && typeof(obj.baz) == 'string'){
        const _:FullType =  obj // TYPE ERROR here
        return true
    } 
    return false
}

@guillaumebrunerie
Copy link

@AxelHaddad Use discriminated unions instead, it is much more sound than regular unions. That is use the following two types:

type PartialType = {isFull: false, foo: string}
type FullType = {isFull: true, foo:string, bar:string, baz: string}

and check the value of isFull.

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Apr 25, 2023
@typescript-bot
Copy link
Collaborator

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

6 participants