Skip to content

Ensure 'in' does not operate on primitive typesΒ #43210

Open
@jonhue

Description

@jonhue

Bug Report

πŸ”Ž Search Terms

operator in exception crash unhandled

πŸ•— Version & Regression Information

This is the behavior in every version I tried

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

const mustBeObject = (thing: object) => thing

const hasKey = <A extends object | null | string | number, K extends string | number | symbol>(
    thing: A,
    key: K,
): boolean => {
    if (thing && typeof thing === 'object') {
        mustBeObject(thing);
        return key in thing; // no error
    }
    return key in thing; // error
};

hasKey(123, 'hello'); 

πŸ™ Actual behavior

TS should expect A to be an object

πŸ™‚ Expected behavior

TS didn't detect the potential crash


This is a follow-up to #41317. In #41928, we decided that we should implement a more conservative check that only ensures that the resolved constraint of the right operand to the in operator is not assignable to a primitive. This is a negative check ensuring that the type of the right operand does not explicitly extend a primitive type. This solution does not yet cover the original example shown above.

The alternative we discussed was a positive check that the type of the right expression cannot possibly extend a primitive type. We could do this either by checking that the type of right operand (not its resolved constraint!) is not assignable to a primitive or by checking that this type is assignable to object.

I am reopening this issue because with #43183 (πŸŽ‰) the original reason why we went with the more conservative (from a breaking change point of view) check will soon not be a limitation any more. Therefore, code like

   if (typeof val === 'object' && '__isMaybe' in val) {

does not represent a problem anymore because val can be narrowed even if its type is a type parameter as long as it extends a union (see #41928 (comment) for the original discussion).

Metadata

Metadata

Assignees

No one assigned

    Labels

    In DiscussionNot yet reached consensusSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions