-
Notifications
You must be signed in to change notification settings - Fork 12.8k
consistently check expressions with >, >=, <, <= for unconstrained types in strictNullChecks #59352
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
base: main
Are you sure you want to change the base?
consistently check expressions with >, >=, <, <= for unconstrained types in strictNullChecks #59352
Conversation
@typescript-bot test it |
Hey @iisaduan, I've packed this into an installable tgz. You can install it for testing by referencing it in your
and then running There is also a playground for this build and an npm module you can use via |
Hey @iisaduan, the results of running the DT tests are ready. Everything looks the same! |
@iisaduan Here are the results of running the user tests with tsc comparing Everything looks good! |
@iisaduan Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
@iisaduan Here are the results of running the top 400 repos with tsc comparing Something interesting changed - please have a look. Details
|
tests/baselines/reference/unconstrainedTypeComparison.errors.txt
Outdated
Show resolved
Hide resolved
src/compiler/checker.ts
Outdated
@@ -27493,6 +27493,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { | |||
return type === unknownUnionType ? unknownType : type; | |||
} | |||
|
|||
// use to determine if a parameter may be undefined or null (or is unknown/unconstrained) | |||
function getUnknownIfMaybeUnknown(type: Type) { | |||
return (strictNullChecks && type.flags & TypeFlags.Instantiable) ? getBaseConstraintOfType(type) || unknownType : type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm assuming from #59059 that making this change to getTypeFacts
breaks a lot of existing code, is that right?
Also, does making this change to checkNonNullType
instead also break a lot of code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I tried that and putting this change directly into checkNonNullType
gets a very similar result to #59059 in terms of breaking object index checking
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of the cases that break relate to this type of index access #59059 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if this it's noticeably different in semantics, but istead of doing this, you might want to try to modify getBaseTypeOfLiteralTypeForComparison
and rename it getBaseTypeForComparison
. Then pass the result of that into checkNonNullType
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also consider renaming this to getBaseConstraintOrUnknown
, use ??
instead of ||
, and just get rid of the strictNullChecks
check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also - any clue if this works on unions of unconstrained type parameters? Can you add a test?
function f<T, U>(x: T | U, y: T | U) {
return x < y;
}
Checking this @typescript-bot perf test this |
src/compiler/checker.ts
Outdated
@@ -27493,6 +27493,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { | |||
return type === unknownUnionType ? unknownType : type; | |||
} | |||
|
|||
// use to determine if a parameter may be undefined or null (or is unknown/unconstrained) | |||
function getUnknownIfMaybeUnknown(type: Type) { | |||
return (strictNullChecks && type.flags & TypeFlags.Instantiable) ? getBaseConstraintOfType(type) || unknownType : type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if this it's noticeably different in semantics, but istead of doing this, you might want to try to modify getBaseTypeOfLiteralTypeForComparison
and rename it getBaseTypeForComparison
. Then pass the result of that into checkNonNullType
.
src/compiler/checker.ts
Outdated
@@ -27493,6 +27493,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { | |||
return type === unknownUnionType ? unknownType : type; | |||
} | |||
|
|||
// use to determine if a parameter may be undefined or null (or is unknown/unconstrained) | |||
function getUnknownIfMaybeUnknown(type: Type) { | |||
return (strictNullChecks && type.flags & TypeFlags.Instantiable) ? getBaseConstraintOfType(type) || unknownType : type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also consider renaming this to getBaseConstraintOrUnknown
, use ??
instead of ||
, and just get rid of the strictNullChecks
check.
src/compiler/checker.ts
Outdated
@@ -27493,6 +27493,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { | |||
return type === unknownUnionType ? unknownType : type; | |||
} | |||
|
|||
// use to determine if a parameter may be undefined or null (or is unknown/unconstrained) | |||
function getUnknownIfMaybeUnknown(type: Type) { | |||
return (strictNullChecks && type.flags & TypeFlags.Instantiable) ? getBaseConstraintOfType(type) || unknownType : type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also - any clue if this works on unions of unconstrained type parameters? Can you add a test?
function f<T, U>(x: T | U, y: T | U) {
return x < y;
}
@DanielRosenwasser Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
tsserverComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
startupComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
@DanielRosenwasser Thanks for the suggestions! Good catch--it does work for unions but not intersections, so I added those cases as well. |
@typescript-bot test it |
@gabritto Here are the results of running the user tests with tsc comparing Everything looks good! |
Hey @gabritto, the results of running the DT tests are ready. Everything looks the same! |
@gabritto Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
@gabritto Here are the results of running the top 400 repos with tsc comparing Something interesting changed - please have a look. Details
|
I have just put up #59437 which I believe is the right fix for #50603 plus the inconsistencies related to unconstrained type parameters vs. type parameters constrained to |
fixes #50603, which bisected to #49119
Previously: unconstrained type parameters were inconsistently checked in comparisons, as shown below (5.5.3 playground):
Now with this PR:
This PR does not affect the checking of unconstrained types in other locations.