-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Add the comparability relationship to the spec #17215
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
Conversation
type A = string | number | boolean;
type B = string;
var a:A;
var b:B;
a=b;
b=a; //it was error in 1.8.0 It was an error at the last line of snippet in 1.8 version, but now it is not. Is it effect of CFA or something else? I don't think that it is a bug but I think the spec lacks of description of that. Quick info shows us that the type of |
@olegdunkan this isn't really related to comparability; we still use assignability in assignments. Like you said, it's a result of control flow analyzed types, as we also use the narrowed type of |
Yes, I understand |
This needs to update sections like union types as well. |
|
||
*TODO: Document the base primitive type. | ||
|
||
*Types are required to be *comparable* in certain circumstances, such as part of when checking whether two values of given types might be equal at runtime using operators like '===', or when using a type assertion. |
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.
We should mention how this definition is really similar to assignment compatibility, and even probably provide it first as a diff, or maybe afterward as a diff.
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.
Never mind, I found it below.
* *S* and *T* are identical types. | ||
* *S* or *T* is the Any type. | ||
* *S* or *T* is an enum type and the other is the primitive type Number. | ||
* *S* is a literal type and *T* is the base primitive type of S. |
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.
do the new rules for literal types apply here? I need to double-check.
|
||
When comparing call or construct signatures, parameter names are ignored and rest parameters correspond to an unbounded expansion of optional parameters of the rest parameter element type. | ||
|
||
Note that specialized call and construct signatures (section [3.9.2.4](#3.9.2.4)) are not significant when determining assignment compatibility. |
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.
Specialized signatures are not a thing anymore, I think. Though we should check whether the rules for validating overload sets are up to date.
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.
Comparability
*S* is ***comparable to*** a type *T*, and *T* is ***comparable from*** *S*, if one of the following is true: | ||
|
||
* *S* and *T* are identical types. | ||
* *S* or *T* is the Any 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.
simpleTypeRelatedTo
rules go here, overwriting lines 2367-2369. (Note that simpleTypeRelatedTo
is called twice, but I flatten the rules so we don't have to say "only this part is symmetric".)
- S is the never type.
- S is a literal type and T is the base primitive type of S. (note: and vice versa for comparability)
- S is an enum member with a literal type and T is a literal type that is not an enum member, and the literal values of S and T are the same. (note: and vice versa, for comparability)
- S and T are enum members and come from the same enum.
- S and T are enum members with a literal type and the literal values of S and T are the same, and S and T come from the same enum.
- S and T are the undefined or void types. (note: for assignability, the rule is "S is the undefined type and T is the undefined or void types.")
- S and T are the null type.
- S or T is an object type and the other is the
object
type. (note: assignability is "S is an object type and T is theobject
type.") - S or T is an enum type and the other is the number type or a numeric literal type that is not the type of an enum member.
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.
We need a section that describes how enums "come from the same enum" - because we really need an enum relation section.
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 "S and T are enum types and come from the same enum" instead of "S and T are enum members ...".
* *S* or *T* is the Any type. | ||
* *S* or *T* is an enum type and the other is the primitive type Number. | ||
* *S* is a literal type and *T* is the base primitive type of S. | ||
* *S* is a union type and some constituent type of *S* is comparable to *T*. |
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.
TODO: Excess property check goes above this line.
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 believe that's relevant to comparability.
* *S* is an intersection type and at least one constituent type of *S* is comparable to *T*. | ||
* *T* is a union type and *S* is comparable to at least one constituent type of *T*. | ||
* *T* is an intersection type and *S* is comparable to each constituent type of *T*. | ||
* *S* is a type parameter and the constraint of *S* is comparable to *T*. |
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.
Above this line:
- T is a type parameter and S is mapped type
{ [P in keyof T]: X }
and X is related to T[P]. - T is a keyof type
keyof T'
and- S is a keyof type
keyof S'
and T' is comparable to S', or - S is related to
keyof C
, where C is the constraint of T'.
- S is a keyof type
- T is an indexed access type
T'[K]
and S is related to C[K], where C is the constraint of T'. - S is a mapped type
{ [P in keyof S]: X }
and S[P] is related to X.
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.
On this line, append "and T is not the object
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.
Below this line:
- S is an indexed access type
S'[K]
andC[K]
is related to T, where C is the constraint of S'.
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 not sure what some of these corrections refer to, but should
- S is a mapped type
{ [P in keyof S]: X }
and S[P] is related to X.
be
- T is a mapped type { [P in K]: X }, S is not a mapped type, keyof S is identical to K, and S[K] is comparable to X.
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, looks like I just messed that one up. It should be "S[P] is comparable to X", though. You could write it more compactly as:
- T is a mapped type { [P in keyof S]: X } and S[P] is comparable to X, where S is not a mapped type.
Since you can express the identity of K and keyof S implicitly.
* *M* is a non-specialized call or construct signature and *S* has an apparent call or construct signature *N* where, when *M* and *N* are instantiated using type Any as the type argument for all type parameters declared by *M* and *N* (if any), | ||
* the signatures are of the same kind (call or construct), | ||
* *M* has a rest parameter or the number of non-optional parameters in *N* is less than or equal to the total number of parameters in *M*, | ||
* for parameter positions that are present in both signatures, each parameter type in *N* is comparable to or from the corresponding parameter type in *M*, and |
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.
TODO: Doesn't cover this-parameters.
TODO: Doesn't cover relatability of spreads to non-spread parameters.
TODO: Doesn't cover stricter variance checks.
We can probably merge without these, but I'll take a look at them next.
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.
- if M has a this-parameter whose type is not the void type, then it must be comparable to the this-parameter type of N or N must not have a this-parameter.
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.
@DanielRosenwasser can we get these in? |
Closing in favor of #20686 |
It's been a while since we've updated the spec, so this is me taking my first swing at it. I'm batching up updates in the
specUpdate
branch, and will commit/merge the finaldocx
andpdf
files when it's ready. @ahejlsberg, I'd like to get some feedback on here or offline given these changes.My first update is the comparability relationship since that's something I have a lot of background on. Eventually, I'd like to branch into other more involved topics.
Fixes #17214.