-
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -84,10 +84,13 @@ TypeScript is a trademark of Microsoft Corporation. | |
* [3.11.2 Type and Member Identity](#3.11.2) | ||
* [3.11.3 Subtypes and Supertypes](#3.11.3) | ||
* [3.11.4 Assignment Compatibility](#3.11.4) | ||
* [3.11.5 Excess Properties](#3.11.5) | ||
* [3.11.6 Contextual Signature Instantiation](#3.11.6) | ||
* [3.11.7 Type Inference](#3.11.7) | ||
* [3.11.8 Recursive Types](#3.11.8) | ||
* [3.11.5 Comparability](#3.11.5) | ||
* [3.11.6 Equality Comparison](#3.11.6) | ||
* [3.11.7 Weak Types](#3.11.7) | ||
* [3.11.8 Excess Properties](#3.11.8) | ||
* [3.11.9 Contextual Signature Instantiation](#3.11.9) | ||
* [3.11.10 Type Inference](#3.11.10) | ||
* [3.11.11 Recursive Types](#3.11.11) | ||
* [3.12 Widened Types](#3.12) | ||
* [4 Expressions](#4) | ||
* [4.1 Values and References](#4.1) | ||
|
@@ -2262,7 +2265,7 @@ the variables 'a' and 'b' are of identical types because the two type references | |
|
||
### <a name="3.11.3"/>3.11.3 Subtypes and Supertypes | ||
|
||
*S* is a ***subtype*** of a type *T*, and *T* is a ***supertype*** of *S*, if *S* has no excess properties with respect to *T* ([3.11.5](#3.11.5)) and one of the following is true: | ||
*S* is a ***subtype*** of a type *T*, and *T* is a ***supertype*** of *S*, if *S* has no excess properties with respect to *T* ([3.11.8](#3.11.8)) and one of the following is true: | ||
|
||
* *S* and *T* are identical types. | ||
* *T* is the Any type. | ||
|
@@ -2297,9 +2300,13 @@ Also note that type parameters are not considered object types. Thus, the only s | |
|
||
### <a name="3.11.4"/>3.11.4 Assignment Compatibility | ||
|
||
*TODO: Document weak type checks. | ||
|
||
TODO: Document signature instantiation* | ||
|
||
Types are required to be assignment compatible in certain circumstances, such as expression and variable types in assignment statements and argument and parameter types in function calls. | ||
|
||
*S* is ***assignable to*** a type *T*, and *T* is ***assignable from*** *S*, if *S* has no excess properties with respect to *T* ([3.11.5](#3.11.5)) and one of the following is true: | ||
*S* is ***assignable to*** a type *T*, and *T* is ***assignable from*** *S*, if *S* has no excess properties with respect to *T* ([3.11.8](#3.11.8)) and one of the following is true: | ||
|
||
* *S* and *T* are identical types. | ||
* *S* or *T* is the Any type. | ||
|
@@ -2348,7 +2355,62 @@ foo({ id: 1234, name: false }); // Error, name of wrong type | |
foo({ name: "hello" }); // Error, id required but missing | ||
``` | ||
|
||
### <a name="3.11.5"/>3.11.5 Excess Properties | ||
### <a name="3.11.5"/>3.11.5 Comparability | ||
|
||
*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. | ||
|
||
*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 commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe 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 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 commentThe 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. |
||
* *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 commentThe 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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. Above this line:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On this line, append "and T is not the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Below this line:
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
be
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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:
Since you can express the identity of K and keyof S implicitly. |
||
* *S* is an object type, an intersection type, an enum type, or the Number, Boolean, or String primitive type, *T* is an object type, and for each member *M* in *T*, one of the following is true: | ||
* *M* is a property and *S* has an apparent property *N* where | ||
* *M* and *N* have the same name, | ||
* the type of *N* is comparable to that of *M*, | ||
* *M* and *N* are both public, *M* and *N* are both private and originate in the same declaration, *M* and *N* are both protected and originate in the same declaration, or *M* is protected and *N* is declared in a class derived from the class in which *M* is declared. | ||
* *M* is an optional property and *S* has no apparent property of the same name as *M*. | ||
* *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 commentThe reason will be displayed to describe this comment to others. Learn more. TODO: Doesn't cover this-parameters. 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 commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
* the result type of *M* is Void, or the result type of *N* is comparable to that of *M*. | ||
* *M* is a string index signature of type *U*, and *U* is the Any type or *S* has an apparent string index signature of a type that is comparable to *U*. | ||
* *M* is a numeric index signature of type *U*, and *U* is the Any type or *S* has an apparent string or numeric index signature of a type that is comparable to *U*. | ||
|
||
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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. Comparability |
||
|
||
The comparability and assignment compatibility rules differ only in that | ||
|
||
* a union type is comparable to a target type if any of its constituents, rather than all of its constituents, are comparable to that target type, | ||
* whether a type is considered weak ([3.11.7](#3.11.7)) has no bearing on whether two types are comparable, | ||
* whether an object type has excess properties ([3.11.8](#3.11.8)) has no bearing on whether two types are comparable, | ||
* an object type with some optional property is comparable to an object type containing a property of the same name if it has a compatible type, regardless of whether that property is optional, and | ||
* when relating any two signatures, each signature is always instantiated using type Any for all type arguments. | ||
|
||
While the comparable relation is often applied bidirectionally on a pair of types, it is not a symmetric relationship. | ||
|
||
*TODO: Give an example of the comparable relation.* | ||
|
||
### <a name="3.11.6"/>3.11.6 Equality Comparison | ||
|
||
*TODO: Document cases where '===' etc. permit targets of Null and Undefined.* | ||
|
||
### <a name="3.11.7"/>3.11.7 Weak Types | ||
|
||
*TODO* | ||
|
||
### <a name="3.11.8"/>3.11.8 Excess Properties | ||
|
||
The subtype and assignment compatibility relationships require that source types have no excess properties with respect to their target types. The purpose of this check is to detect excess or misspelled properties in object literals. | ||
|
||
|
@@ -2404,14 +2466,14 @@ var address: InputElement = { | |
}; | ||
``` | ||
|
||
### <a name="3.11.6"/>3.11.6 Contextual Signature Instantiation | ||
### <a name="3.11.9"/>3.11.9 Contextual Signature Instantiation | ||
|
||
During type argument inference in a function call (section [4.15.2](#4.15.2)) it is in certain circumstances necessary to instantiate a generic call signature of an argument expression in the context of a non-generic call signature of a parameter such that further inferences can be made. A generic call signature *A* is ***instantiated in the context of*** non-generic call signature *B* as follows: | ||
|
||
* Using the process described in [3.11.7](#3.11.7), inferences for *A*'s type parameters are made from each parameter type in *B* to the corresponding parameter type in *A* for those parameter positions that are present in both signatures, where rest parameters correspond to an unbounded expansion of optional parameters of the rest parameter element type. | ||
* Using the process described in [3.11.10](#3.11.10), inferences for *A*'s type parameters are made from each parameter type in *B* to the corresponding parameter type in *A* for those parameter positions that are present in both signatures, where rest parameters correspond to an unbounded expansion of optional parameters of the rest parameter element type. | ||
* The inferred type argument for each type parameter is the union type of the set of inferences made for that type parameter. However, if the union type does not satisfy the constraint of the type parameter, the inferred type argument is instead the constraint. | ||
|
||
### <a name="3.11.7"/>3.11.7 Type Inference | ||
### <a name="3.11.10"/>3.11.10 Type Inference | ||
|
||
In certain contexts, inferences for a given set of type parameters are made *from* a type *S*, in which those type parameters do not occur, *to* another type *T*, in which those type parameters do occur. Inferences consist of a set of candidate type arguments collected for each of the type parameters. The inference process recursively relates *S* and *T* to gather as many inferences as possible: | ||
|
||
|
@@ -2434,7 +2496,7 @@ When comparing call or construct signatures, signatures in *S* correspond to sig | |
|
||
*TODO: Update to reflect [improved union and intersection type inference](https://github.com/Microsoft/TypeScript/pull/5738)*. | ||
|
||
### <a name="3.11.8"/>3.11.8 Recursive Types | ||
### <a name="3.11.11"/>3.11.11 Recursive Types | ||
|
||
Classes and interfaces can reference themselves in their internal structure, in effect creating recursive types with infinite nesting. For example, the type | ||
|
||
|
@@ -2470,6 +2532,8 @@ interface List<T> { | |
|
||
## <a name="3.12"/>3.12 Widened Types | ||
|
||
*TODO: Update for literal type widening.* | ||
|
||
In several situations TypeScript infers types from context, alleviating the need for the programmer to explicitly specify types that appear obvious. For example | ||
|
||
```TypeScript | ||
|
@@ -2535,6 +2599,8 @@ An identifier expression that references a variable or parameter is classified a | |
|
||
## <a name="4.4"/>4.4 Literals | ||
|
||
*TODO: Update to account for literal types and widening.* | ||
|
||
Literals are typed as follows: | ||
|
||
* The type of the `null` literal is the Null primitive type. | ||
|
@@ -2968,13 +3034,13 @@ Type argument inference produces a set of candidate types for each type paramete | |
In order to compute candidate types, the argument list is processed as follows: | ||
|
||
* Initially all inferred type arguments are considered ***unfixed*** with an empty set of candidate types. | ||
* Proceeding from left to right, each argument expression *e* is ***inferentially typed*** by its corresponding parameter type *P*, possibly causing some inferred type arguments to become ***fixed***, and candidate type inferences (section [3.11.7](#3.11.7)) are made for unfixed inferred type arguments from the type computed for *e* to *P*. | ||
* Proceeding from left to right, each argument expression *e* is ***inferentially typed*** by its corresponding parameter type *P*, possibly causing some inferred type arguments to become ***fixed***, and candidate type inferences (section [3.11.10](#3.11.10)) are made for unfixed inferred type arguments from the type computed for *e* to *P*. | ||
|
||
The process of inferentially typing an expression *e* by a type *T* is the same as that of contextually typing *e* by *T*, with the following exceptions: | ||
|
||
* Where expressions contained within *e* would be contextually typed, they are instead inferentially typed. | ||
* When a function expression is inferentially typed (section [4.10](#4.10)) and a type assigned to a parameter in that expression references type parameters for which inferences are being made, the corresponding inferred type arguments to become ***fixed*** and no further candidate inferences are made for them. | ||
* If *e* is an expression of a function type that contains exactly one generic call signature and no other members, and *T* is a function type with exactly one non-generic call signature and no other members, then any inferences made for type parameters referenced by the parameters of *T*'s call signature are ***fixed***, and *e*'s type is changed to a function type with *e*'s call signature instantiated in the context of *T*'s call signature (section [3.11.6](#3.11.6)). | ||
* If *e* is an expression of a function type that contains exactly one generic call signature and no other members, and *T* is a function type with exactly one non-generic call signature and no other members, then any inferences made for type parameters referenced by the parameters of *T*'s call signature are ***fixed***, and *e*'s type is changed to a function type with *e*'s call signature instantiated in the context of *T*'s call signature (section [3.11.9](#3.11.9)). | ||
|
||
An example: | ||
|
||
|
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.