Description
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).