Skip to content

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

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Jakefile.js
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,10 @@ compileFile(word2mdJs,
[word2mdTs],
[word2mdTs],
[],
/*useBuiltCompiler*/ false);
/*useBuiltCompiler*/ false,
{
lib: "scripthost,es5"
});

// The generated spec.md; built for the 'generate-spec' task
file(specMd, [word2mdJs, specWord], function () {
Expand Down
92 changes: 79 additions & 13 deletions doc/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Copy link
Member

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.

Copy link
Member

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* 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.
Copy link
Member

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 the object 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.

Copy link
Member Author

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.

Copy link
Member

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 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.
Copy link
Member

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.

* *S* is a union type and some constituent type of *S* is comparable to *T*.
Copy link
Member

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.

Copy link
Member Author

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*.
Copy link
Member

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'.
  • 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.

Copy link
Member

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."

Copy link
Member

@sandersn sandersn Dec 4, 2017

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] and C[K] is related to T, where C is the constraint of S'.

Copy link
Member Author

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.

Copy link
Member

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.

* *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
Copy link
Member

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.

Copy link
Member

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.

Copy link
Member Author

Choose a reason for hiding this comment

The 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.
Copy link
Member

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.

Copy link
Member Author

Choose a reason for hiding this comment

The 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.

Expand Down Expand Up @@ -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:

Expand All @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:

Expand Down