tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(19,17): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(23,12): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(27,12): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(34,12): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(53,14): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(60,12): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(64,12): error TS2361: The right-hand side of an 'in' expression must not be a primitive. ==== tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts (7 errors) ==== const validHasKey = ( thing: T, key: string, ): boolean => { return key in thing; // Ok }; const alsoValidHasKey = ( thing: T, key: string, ): boolean => { return key in thing; // Ok (as T may be instantiated with a valid type) }; function invalidHasKey( thing: T, key: string, ): boolean { return key in thing; // Error (because all possible instantiations are errors) ~~~~~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. } function union1(thing: T | U) { "key" in thing; // Error (because all possible instantiations are errors) ~~~~~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. } function union2(thing: T | U) { "key" in thing; // Error (because narrowing is possible) ~~~~~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. if (typeof thing === "object") { "key" in thing; // Ok } } function union3(thing: T | string | number) { "key" in thing; // Error (because narrowing is possible) ~~~~~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. if (typeof thing !== "string" && typeof thing !== "number") { "key" in thing; // Ok (because further narrowing is impossible) } } function union4(thing: T) { "key" in thing; // Ok (because narrowing is impossible) } function union5(p: T | U) { // For consistency, this should probably not be an error, because useful // narrowing is impossible. However, this is exceptionally strange input, // and it adds a lot of complexity to distinguish between a `T | U` where // one constraint is non-primitive and the other is primitive and a `T | U` // like this where both constraints have primitive and non-primitive // constitutents. Also, the strictly sound behavior would be to error // here, which is what's happening, so "fixing" this by suppressing the // error seems very low-value. "key" in p; ~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. if (typeof p === "object") { "key" in p; } } function intersection1(thing: T & U) { "key" in thing; // Error (because all possible instantiations are errors) ~~~~~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. } function intersection2(thing: T & (0 | 1 | 2)) { "key" in thing; // Error (because all possible instantations are errors) ~~~~~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. }