-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Stricter relational comparisons checking valueOf
#52807
Conversation
In the self test:
function compareComparableValues(a: string | undefined, b: string | undefined): Comparison;
function compareComparableValues(a: number | undefined, b: number | undefined): Comparison;
function compareComparableValues(a: string | number | undefined, b: string | number | undefined) {
return a === b ? Comparison.EqualTo :
a === undefined ? Comparison.LessThan :
b === undefined ? Comparison.GreaterThan :
a < b ? Comparison.LessThan :
Comparison.GreaterThan;
} Seems like a great catch to me! The entire thing could/should just be: function compareComparableValues<T extends string | number>(a: T | undefined, b: T | undefined) {
// ....
} EDIT: this is wrong, see below |
@typescript-bot test this |
Heya @RyanCavanaugh, I've started to run the extended test suite on this PR at 08a34e1. You can monitor the build here. |
Heya @RyanCavanaugh, I've started to run the diff-based top-repos suite on this PR at 08a34e1. You can monitor the build here. Update: The results are in! |
Heya @RyanCavanaugh, I've started to run the parallelized Definitely Typed test suite on this PR at 08a34e1. You can monitor the build here. |
Heya @RyanCavanaugh, I've started to run the diff-based user code test suite (tsserver) on this PR at 08a34e1. You can monitor the build here. Update: The results are in! |
Heya @RyanCavanaugh, I've started to run the perf test suite on this PR at 08a34e1. You can monitor the build here. Update: The results are in! |
Heya @RyanCavanaugh, I've started to run the diff-based user code test suite on this PR at 08a34e1. You can monitor the build here. Update: The results are in! |
The case of function compare<T extends string | number>(a: T, b: T) {
return a > b;
} is pretty tough actually, since by our own logic this should be illegal (and is under this PR)
but people will probably feel that it ought to be legal. |
@RyanCavanaugh Here are the results of running the user test suite comparing Something interesting changed - please have a look. Detailspuppeteer
|
@RyanCavanaugh Here are the results of running the user test suite comparing Something interesting changed - please have a look. Details
|
Heya @RyanCavanaugh, I've run the RWC suite on this PR - assuming you're on the TS core team, you can view the resulting diff here. |
RWC diffs are mostly what you'd expect from this:
|
@RyanCavanaugh Here they are:
CompilerComparison Report - main..52807
System
Hosts
Scenarios
TSServerComparison Report - main..52807
System
Hosts
Scenarios
StartupComparison Report - main..52807
System
Hosts
Scenarios
Developer Information: |
@RyanCavanaugh Here are the results of running the top-repos suite comparing Something interesting changed - please have a look. Details
|
Those vscode bugs look real, in particular the |
@typescript-bot pack this |
Heya @jakebailey, I've started to run the tarball bundle task on this PR at 8703e3c. You can monitor the build here. |
@RyanCavanaugh Here are the results of running the top-repos suite comparing Something interesting changed - please have a look. Detailsangular/angular-cli
|
@RyanCavanaugh Here are some more interesting changes from running the top-repos suite Detailsionic-team/ionic-framework
|
@RyanCavanaugh Here are some more interesting changes from running the top-repos suite Detailspnpm/pnpm
|
@RyanCavanaugh Here are some more interesting changes from running the top-repos suite Detailstrpc/trpc
|
Hey @jakebailey, 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 |
The other break is regarding this definition export type Length = { _brand: 'Length'; };
if (length1 > length2) { In reality, a export type Length = { _brand: 'Length'; valueOf(): number }; |
Would they be better served by |
function getValueOfResult(type: Type): Type { | ||
const valueOf = getPropertyOfType(type, "valueOf" as __String); | ||
if (valueOf) { | ||
const signatures = getSignaturesOfType(getTypeOfSymbol(valueOf), ts.SignatureKind.Call); |
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.
FYI annoying auto-import thing here, but should should just be SignatureKind.Call
; sometimes the ts
prefix appears first in the completion list and gets used (and that's one reason why I'm trying to get us to remove them everywhere).
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.
Main no longer has this ts
import so when you merge from main next, you'll have to fix this (and then never have to worry about again).
I watched the design meeting back, and I didn't realize my fix for Given a type variable
If we're going to disallow comparison of these, then I think the error needs to be improved to say why, because without knowing I could have |
Kind of funny but this allows |
function getValueOfResult(type: Type): Type { | ||
const valueOf = getPropertyOfType(type, "valueOf" as __String); | ||
if (valueOf) { | ||
const signatures = getSignaturesOfType(getTypeOfSymbol(valueOf), ts.SignatureKind.Call); |
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.
const signatures = getSignaturesOfType(getTypeOfSymbol(valueOf), ts.SignatureKind.Call); | |
const signatures = getSignaturesOfType(getTypeOfSymbol(valueOf), SignatureKind.Call); | |
We should include |
if (valueOf) { | ||
const signatures = getSignaturesOfType(getTypeOfSymbol(valueOf), ts.SignatureKind.Call); | ||
if (signatures && signatures.length > 0) { | ||
const returnType = getReturnTypeOfSignature(signatures[0]); |
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.
This favors the first signature in an overload list, while usually in places where we're overload-blind today we usually favor the last overload in the list. Maybe signatures[signatures.length - 1]
for consistency? Or, potentially better yet, rather than picking one overload, getUnionType(map(signatures, getReturnTypeOfSignature))
just to hedge (in theory, only overloads that match an all-undefined/empty argument list would actually apply.... but overloads on valueOf
are probably a bit suspicious anyway). I say that, but we'll actually see an overloaded valueOf
for every intersected type eg, number & {myBrand}
is going to have a valueOf
from number
's apparent type and object
's apparent type intersected together, which'll result in two overloads unless they're merged for being identical.
I think this is basically how everyone intuitively thinks about Footnotes
|
This is mostly being addressed by some other PRs |
Per discussion in #52790, this implements the following ruleset for
>
,<
,>=
, and<=
:For each operand expressions, let the "operand type" be:
valueOf
property of the expression's type, if that existsOne of the following must be true:
Otherwise an error is issued