forked from microsoft/TypeScript
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
'in' should not operate on primitive types (microsoft#41928 + @andrew…
…branch) (microsoft#42288) * 'in' should not operate on primitive types * accept baselines of failing tests * review * update error message * check if constraint of right type is assignable to a non primitive or instantiable non primitive * do not throw errors where narrowing is impossible * accept baselines * fix test case failures * Add more accurate comment discussion and document failing edge case in test * Update baselines Co-authored-by: Jonas Hübotter <[email protected]>
- Loading branch information
Showing
10 changed files
with
656 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 93 additions & 0 deletions
93
tests/baselines/reference/inDoesNotOperateOnPrimitiveTypes.errors.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
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(55,18): 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 (8 errors) ==== | ||
const validHasKey = <T extends object>( | ||
thing: T, | ||
key: string, | ||
): boolean => { | ||
return key in thing; // Ok | ||
}; | ||
|
||
const alsoValidHasKey = <T>( | ||
thing: T, | ||
key: string, | ||
): boolean => { | ||
return key in thing; // Ok (as T may be instantiated with a valid type) | ||
}; | ||
|
||
function invalidHasKey<T extends string | number>( | ||
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<T extends string | number, U extends boolean>(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<T extends object, U extends string | number>(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<T>(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<T extends object | "hello">(thing: T) { | ||
"key" in thing; // Ok (because narrowing is impossible) | ||
} | ||
|
||
function union5<T extends object | string, U extends object | number>(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; | ||
~ | ||
!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. | ||
} | ||
} | ||
|
||
function intersection1<T extends number, U extends 0 | 1 | 2>(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<T>(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. | ||
} | ||
|
116 changes: 116 additions & 0 deletions
116
tests/baselines/reference/inDoesNotOperateOnPrimitiveTypes.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
//// [inDoesNotOperateOnPrimitiveTypes.ts] | ||
const validHasKey = <T extends object>( | ||
thing: T, | ||
key: string, | ||
): boolean => { | ||
return key in thing; // Ok | ||
}; | ||
|
||
const alsoValidHasKey = <T>( | ||
thing: T, | ||
key: string, | ||
): boolean => { | ||
return key in thing; // Ok (as T may be instantiated with a valid type) | ||
}; | ||
|
||
function invalidHasKey<T extends string | number>( | ||
thing: T, | ||
key: string, | ||
): boolean { | ||
return key in thing; // Error (because all possible instantiations are errors) | ||
} | ||
|
||
function union1<T extends string | number, U extends boolean>(thing: T | U) { | ||
"key" in thing; // Error (because all possible instantiations are errors) | ||
} | ||
|
||
function union2<T extends object, U extends string | number>(thing: T | U) { | ||
"key" in thing; // Error (because narrowing is possible) | ||
if (typeof thing === "object") { | ||
"key" in thing; // Ok | ||
} | ||
} | ||
|
||
function union3<T>(thing: T | string | number) { | ||
"key" in thing; // Error (because narrowing is possible) | ||
if (typeof thing !== "string" && typeof thing !== "number") { | ||
"key" in thing; // Ok (because further narrowing is impossible) | ||
} | ||
} | ||
|
||
function union4<T extends object | "hello">(thing: T) { | ||
"key" in thing; // Ok (because narrowing is impossible) | ||
} | ||
|
||
function union5<T extends object | string, U extends object | number>(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; | ||
if (typeof p === "object") { | ||
"key" in p; | ||
} | ||
} | ||
|
||
function intersection1<T extends number, U extends 0 | 1 | 2>(thing: T & U) { | ||
"key" in thing; // Error (because all possible instantiations are errors) | ||
} | ||
|
||
function intersection2<T>(thing: T & (0 | 1 | 2)) { | ||
"key" in thing; // Error (because all possible instantations are errors) | ||
} | ||
|
||
|
||
//// [inDoesNotOperateOnPrimitiveTypes.js] | ||
var validHasKey = function (thing, key) { | ||
return key in thing; // Ok | ||
}; | ||
var alsoValidHasKey = function (thing, key) { | ||
return key in thing; // Ok (as T may be instantiated with a valid type) | ||
}; | ||
function invalidHasKey(thing, key) { | ||
return key in thing; // Error (because all possible instantiations are errors) | ||
} | ||
function union1(thing) { | ||
"key" in thing; // Error (because all possible instantiations are errors) | ||
} | ||
function union2(thing) { | ||
"key" in thing; // Error (because narrowing is possible) | ||
if (typeof thing === "object") { | ||
"key" in thing; // Ok | ||
} | ||
} | ||
function union3(thing) { | ||
"key" in thing; // Error (because narrowing is possible) | ||
if (typeof thing !== "string" && typeof thing !== "number") { | ||
"key" in thing; // Ok (because further narrowing is impossible) | ||
} | ||
} | ||
function union4(thing) { | ||
"key" in thing; // Ok (because narrowing is impossible) | ||
} | ||
function union5(p) { | ||
// 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; | ||
if (typeof p === "object") { | ||
"key" in p; | ||
} | ||
} | ||
function intersection1(thing) { | ||
"key" in thing; // Error (because all possible instantiations are errors) | ||
} | ||
function intersection2(thing) { | ||
"key" in thing; // Error (because all possible instantations are errors) | ||
} |
Oops, something went wrong.