Skip to content

Commit 9705c89

Browse files
Merge pull request #5517 from Microsoft/comparableRelation
Introduce the "comparable" relation
2 parents 0556b15 + 3cc64cb commit 9705c89

File tree

43 files changed

+780
-99
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+780
-99
lines changed

src/compiler/checker.ts

+34-15
Original file line numberDiff line numberDiff line change
@@ -5397,6 +5397,10 @@ namespace ts {
53975397
return checkTypeAssignableTo(source, target, /*errorNode*/ undefined);
53985398
}
53995399

5400+
/**
5401+
* This is *not* a bi-directional relationship.
5402+
* If one needs to check both directions for comparability, use a second call to this function or 'checkTypeComparableTo'.
5403+
*/
54005404
function isTypeComparableTo(source: Type, target: Type): boolean {
54015405
return checkTypeComparableTo(source, target, /*errorNode*/ undefined);
54025406
}
@@ -5409,6 +5413,10 @@ namespace ts {
54095413
return checkTypeRelatedTo(source, target, assignableRelation, errorNode, headMessage, containingMessageChain);
54105414
}
54115415

5416+
/**
5417+
* This is *not* a bi-directional relationship.
5418+
* If one needs to check both directions for comparability, use a second call to this function or 'isTypeComparableTo'.
5419+
*/
54125420
function checkTypeComparableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean {
54135421
return checkTypeRelatedTo(source, target, comparableRelation, errorNode, headMessage, containingMessageChain);
54145422
}
@@ -5619,7 +5627,14 @@ namespace ts {
56195627
sourceType = typeToString(source, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType);
56205628
targetType = typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType);
56215629
}
5622-
reportError(message || Diagnostics.Type_0_is_not_assignable_to_type_1, sourceType, targetType);
5630+
5631+
if (!message) {
5632+
message = relation === comparableRelation ?
5633+
Diagnostics.Type_0_is_not_comparable_to_type_1 :
5634+
Diagnostics.Type_0_is_not_assignable_to_type_1;
5635+
}
5636+
5637+
reportError(message, sourceType, targetType);
56235638
}
56245639

56255640
// Compare two types and return
@@ -5648,10 +5663,12 @@ namespace ts {
56485663
}
56495664
}
56505665
if (source.flags & TypeFlags.StringLiteral && target === stringType) return Ternary.True;
5666+
56515667
if (relation === assignableRelation || relation === comparableRelation) {
56525668
if (isTypeAny(source)) return Ternary.True;
56535669
if (source === numberType && target.flags & TypeFlags.Enum) return Ternary.True;
56545670
}
5671+
56555672
if (source.flags & TypeFlags.Boolean && target.flags & TypeFlags.Boolean) {
56565673
return Ternary.True;
56575674
}
@@ -5674,26 +5691,28 @@ namespace ts {
56745691

56755692
const saveErrorInfo = errorInfo;
56765693

5677-
// Note that the "each" checks must precede the "some" checks to produce the correct results
5694+
// Note that these checks are specifically ordered to produce correct results.
56785695
if (source.flags & TypeFlags.Union) {
56795696
if (relation === comparableRelation) {
5680-
if (result = someTypeRelatedToType(<UnionType>source, target, reportErrors)) {
5681-
return result;
5682-
}
5697+
result = someTypeRelatedToType(source as UnionType, target, reportErrors);
56835698
}
56845699
else {
5685-
if (result = eachTypeRelatedToType(<UnionType>source, target, reportErrors)) {
5686-
return result;
5687-
}
5700+
result = eachTypeRelatedToType(source as UnionType, target, reportErrors);
5701+
}
5702+
5703+
if (result) {
5704+
return result;
56885705
}
56895706
}
56905707
else if (target.flags & TypeFlags.Intersection) {
5691-
if (result = typeRelatedToEachType(source, <IntersectionType>target, reportErrors)) {
5708+
result = typeRelatedToEachType(source, target as IntersectionType, reportErrors);
5709+
5710+
if (result) {
56925711
return result;
56935712
}
56945713
}
56955714
else {
5696-
// It is necessary to try "some" checks on both sides because there may be nested "each" checks
5715+
// It is necessary to try these "some" checks on both sides because there may be nested "each" checks
56975716
// on either side that need to be prioritized. For example, A | B = (A | B) & (C | D) or
56985717
// A & B = (A & B) | (C & D).
56995718
if (source.flags & TypeFlags.Intersection) {
@@ -5775,8 +5794,8 @@ namespace ts {
57755794
}
57765795
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union ||
57775796
source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
5778-
if (result = eachTypeRelatedToSomeType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target)) {
5779-
if (result &= eachTypeRelatedToSomeType(<UnionOrIntersectionType>target, <UnionOrIntersectionType>source)) {
5797+
if (result = eachTypeRelatedToSomeType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target, /*reportErrors*/ false)) {
5798+
if (result &= eachTypeRelatedToSomeType(<UnionOrIntersectionType>target, <UnionOrIntersectionType>source, /*reportErrors*/ false)) {
57805799
return result;
57815800
}
57825801
}
@@ -5827,7 +5846,7 @@ namespace ts {
58275846
return false;
58285847
}
58295848

5830-
function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType): Ternary {
5849+
function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType, reportErrors: boolean): Ternary {
58315850
let result = Ternary.True;
58325851
const sourceTypes = source.types;
58335852
for (const sourceType of sourceTypes) {
@@ -11015,7 +11034,7 @@ namespace ts {
1101511034
if (produceDiagnostics && targetType !== unknownType) {
1101611035
const widenedType = getWidenedType(exprType);
1101711036
if (!isTypeComparableTo(targetType, widenedType)) {
11018-
checkTypeComparableTo(exprType, targetType, node, Diagnostics.Neither_type_0_nor_type_1_is_assignable_to_the_other);
11037+
checkTypeComparableTo(exprType, targetType, node, Diagnostics.Type_0_cannot_be_converted_to_type_1);
1101911038
}
1102011039
}
1102111040
return targetType;
@@ -14458,7 +14477,7 @@ namespace ts {
1445814477

1445914478
if (produceDiagnostics && clause.kind === SyntaxKind.CaseClause) {
1446014479
const caseClause = <CaseClause>clause;
14461-
// TypeScript 1.0 spec (April 2014):5.9
14480+
// TypeScript 1.0 spec (April 2014): 5.9
1446214481
// In a 'switch' statement, each 'case' expression must be of a type that is comparable
1446314482
// to or from the type of the 'switch' expression.
1446414483
const caseType = checkExpression(caseClause.expression);

src/compiler/diagnosticMessages.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -1047,7 +1047,7 @@
10471047
"category": "Error",
10481048
"code": 2351
10491049
},
1050-
"Neither type '{0}' nor type '{1}' is assignable to the other.": {
1050+
"Type '{0}' cannot be converted to type '{1}'.": {
10511051
"category": "Error",
10521052
"code": 2352
10531053
},
@@ -1875,6 +1875,10 @@
18751875
"category": "Error",
18761876
"code": 2677
18771877
},
1878+
"Type '{0}' is not comparable to type '{1}'.": {
1879+
"category": "Error",
1880+
"code": 2678
1881+
},
18781882
"Import declaration '{0}' is using private name '{1}'.": {
18791883
"category": "Error",
18801884
"code": 4000

tests/baselines/reference/arrayCast.errors.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
tests/cases/compiler/arrayCast.ts(3,23): error TS2352: Neither type '{ foo: string; }[]' nor type '{ id: number; }[]' is assignable to the other.
2-
Type '{ foo: string; }' is not assignable to type '{ id: number; }'.
1+
tests/cases/compiler/arrayCast.ts(3,23): error TS2352: Type '{ foo: string; }[]' cannot be converted to type '{ id: number; }[]'.
2+
Type '{ foo: string; }' is not comparable to type '{ id: number; }'.
33
Object literal may only specify known properties, and 'foo' does not exist in type '{ id: number; }'.
44

55

@@ -8,8 +8,8 @@ tests/cases/compiler/arrayCast.ts(3,23): error TS2352: Neither type '{ foo: stri
88
// has type { foo: string }[], which is not assignable to { id: number }[].
99
<{ id: number; }[]>[{ foo: "s" }];
1010
~~~~~~~~
11-
!!! error TS2352: Neither type '{ foo: string; }[]' nor type '{ id: number; }[]' is assignable to the other.
12-
!!! error TS2352: Type '{ foo: string; }' is not assignable to type '{ id: number; }'.
11+
!!! error TS2352: Type '{ foo: string; }[]' cannot be converted to type '{ id: number; }[]'.
12+
!!! error TS2352: Type '{ foo: string; }' is not comparable to type '{ id: number; }'.
1313
!!! error TS2352: Object literal may only specify known properties, and 'foo' does not exist in type '{ id: number; }'.
1414

1515
// Should succeed, as the {} element causes the type of the array to be {}[]
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
tests/cases/conformance/expressions/asOperator/asOperator2.ts(1,9): error TS2352: Neither type 'number' nor type 'string' is assignable to the other.
1+
tests/cases/conformance/expressions/asOperator/asOperator2.ts(1,9): error TS2352: Type 'number' cannot be converted to type 'string'.
22

33

44
==== tests/cases/conformance/expressions/asOperator/asOperator2.ts (1 errors) ====
55
var x = 23 as string;
66
~~~~~~~~~~~~
7-
!!! error TS2352: Neither type 'number' nor type 'string' is assignable to the other.
7+
!!! error TS2352: Type 'number' cannot be converted to type 'string'.
88

Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
tests/cases/conformance/expressions/asOperator/asOperatorContextualType.ts(2,9): error TS2352: Neither type '(v: number) => number' nor type '(x: number) => string' is assignable to the other.
2-
Type 'number' is not assignable to type 'string'.
1+
tests/cases/conformance/expressions/asOperator/asOperatorContextualType.ts(2,9): error TS2352: Type '(v: number) => number' cannot be converted to type '(x: number) => string'.
2+
Type 'number' is not comparable to type 'string'.
33

44

55
==== tests/cases/conformance/expressions/asOperator/asOperatorContextualType.ts (1 errors) ====
66
// should error
77
var x = (v => v) as (x: number) => string;
88
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9-
!!! error TS2352: Neither type '(v: number) => number' nor type '(x: number) => string' is assignable to the other.
10-
!!! error TS2352: Type 'number' is not assignable to type 'string'.
9+
!!! error TS2352: Type '(v: number) => number' cannot be converted to type '(x: number) => string'.
10+
!!! error TS2352: Type 'number' is not comparable to type 'string'.
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
tests/cases/conformance/expressions/asOperator/asOperatorNames.ts(2,9): error TS2352: Neither type 'number' nor type 'string' is assignable to the other.
1+
tests/cases/conformance/expressions/asOperator/asOperatorNames.ts(2,9): error TS2352: Type 'number' cannot be converted to type 'string'.
22

33

44
==== tests/cases/conformance/expressions/asOperator/asOperatorNames.ts (1 errors) ====
55
var a = 20;
66
var b = a as string;
77
~~~~~~~~~~~
8-
!!! error TS2352: Neither type 'number' nor type 'string' is assignable to the other.
8+
!!! error TS2352: Type 'number' cannot be converted to type 'string'.
99
var as = "hello";
1010
var as1 = as as string;
1111

tests/baselines/reference/castingTuple.errors.txt

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
tests/cases/conformance/types/tuple/castingTuple.ts(28,10): error TS2352: Neither type '[number, string]' nor type '[number, number]' is assignable to the other.
1+
tests/cases/conformance/types/tuple/castingTuple.ts(28,10): error TS2352: Type '[number, string]' cannot be converted to type '[number, number]'.
22
Types of property '1' are incompatible.
3-
Type 'string' is not assignable to type 'number'.
4-
tests/cases/conformance/types/tuple/castingTuple.ts(29,10): error TS2352: Neither type '[C, D]' nor type '[A, I]' is assignable to the other.
3+
Type 'string' is not comparable to type 'number'.
4+
tests/cases/conformance/types/tuple/castingTuple.ts(29,10): error TS2352: Type '[C, D]' cannot be converted to type '[A, I]'.
55
Types of property '0' are incompatible.
6-
Type 'C' is not assignable to type 'A'.
6+
Type 'C' is not comparable to type 'A'.
77
Property 'a' is missing in type 'C'.
88
tests/cases/conformance/types/tuple/castingTuple.ts(30,5): error TS2403: Subsequent variable declarations must have the same type. Variable 'array1' must be of type '{}[]', but here has type 'number[]'.
99
tests/cases/conformance/types/tuple/castingTuple.ts(31,1): error TS2304: Cannot find name 't4'.
@@ -39,14 +39,14 @@ tests/cases/conformance/types/tuple/castingTuple.ts(31,1): error TS2304: Cannot
3939
// error
4040
var t3 = <[number, number]>numStrTuple;
4141
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
42-
!!! error TS2352: Neither type '[number, string]' nor type '[number, number]' is assignable to the other.
42+
!!! error TS2352: Type '[number, string]' cannot be converted to type '[number, number]'.
4343
!!! error TS2352: Types of property '1' are incompatible.
44-
!!! error TS2352: Type 'string' is not assignable to type 'number'.
44+
!!! error TS2352: Type 'string' is not comparable to type 'number'.
4545
var t9 = <[A, I]>classCDTuple;
4646
~~~~~~~~~~~~~~~~~~~~
47-
!!! error TS2352: Neither type '[C, D]' nor type '[A, I]' is assignable to the other.
47+
!!! error TS2352: Type '[C, D]' cannot be converted to type '[A, I]'.
4848
!!! error TS2352: Types of property '0' are incompatible.
49-
!!! error TS2352: Type 'C' is not assignable to type 'A'.
49+
!!! error TS2352: Type 'C' is not comparable to type 'A'.
5050
!!! error TS2352: Property 'a' is missing in type 'C'.
5151
var array1 = <number[]>numStrTuple;
5252
~~~~~~
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
tests/cases/compiler/contextualTyping39.ts(1,11): error TS2352: Neither type '() => string' nor type '() => number' is assignable to the other.
2-
Type 'string' is not assignable to type 'number'.
1+
tests/cases/compiler/contextualTyping39.ts(1,11): error TS2352: Type '() => string' cannot be converted to type '() => number'.
2+
Type 'string' is not comparable to type 'number'.
33

44

55
==== tests/cases/compiler/contextualTyping39.ts (1 errors) ====
66
var foo = <{ (): number; }> function() { return "err"; };
77
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8-
!!! error TS2352: Neither type '() => string' nor type '() => number' is assignable to the other.
9-
!!! error TS2352: Type 'string' is not assignable to type 'number'.
8+
!!! error TS2352: Type '() => string' cannot be converted to type '() => number'.
9+
!!! error TS2352: Type 'string' is not comparable to type 'number'.
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
tests/cases/compiler/contextualTyping41.ts(1,11): error TS2352: Neither type '() => string' nor type '{ (): number; (i: number): number; }' is assignable to the other.
2-
Type 'string' is not assignable to type 'number'.
1+
tests/cases/compiler/contextualTyping41.ts(1,11): error TS2352: Type '() => string' cannot be converted to type '{ (): number; (i: number): number; }'.
2+
Type 'string' is not comparable to type 'number'.
33

44

55
==== tests/cases/compiler/contextualTyping41.ts (1 errors) ====
66
var foo = <{():number; (i:number):number; }> (function(){return "err";});
77
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8-
!!! error TS2352: Neither type '() => string' nor type '{ (): number; (i: number): number; }' is assignable to the other.
9-
!!! error TS2352: Type 'string' is not assignable to type 'number'.
8+
!!! error TS2352: Type '() => string' cannot be converted to type '{ (): number; (i: number): number; }'.
9+
!!! error TS2352: Type 'string' is not comparable to type 'number'.

tests/baselines/reference/defaultArgsInFunctionExpressions.errors.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ tests/cases/compiler/defaultArgsInFunctionExpressions.ts(4,19): error TS2345: Ar
22
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(5,1): error TS2322: Type 'number' is not assignable to type 'string'.
33
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(8,20): error TS2322: Type 'number' is not assignable to type 'string'.
44
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(11,1): error TS2322: Type 'string' is not assignable to type 'number'.
5-
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(14,51): error TS2352: Neither type 'string' nor type 'number' is assignable to the other.
5+
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(14,51): error TS2352: Type 'string' cannot be converted to type 'number'.
66
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(17,41): error TS2322: Type 'string' is not assignable to type 'number'.
7-
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(20,62): error TS2352: Neither type 'string' nor type 'number' is assignable to the other.
7+
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(20,62): error TS2352: Type 'string' cannot be converted to type 'number'.
88
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(28,15): error TS2304: Cannot find name 'T'.
99

1010

@@ -32,7 +32,7 @@ tests/cases/compiler/defaultArgsInFunctionExpressions.ts(28,15): error TS2304: C
3232
// Contextually type the default arg with the type annotation
3333
var f3 = function (a: (s: string) => any = (s) => <number>s) { };
3434
~~~~~~~~~
35-
!!! error TS2352: Neither type 'string' nor type 'number' is assignable to the other.
35+
!!! error TS2352: Type 'string' cannot be converted to type 'number'.
3636

3737
// Type check using the function's contextual type
3838
var f4: (a: number) => void = function (a = "") { };
@@ -42,7 +42,7 @@ tests/cases/compiler/defaultArgsInFunctionExpressions.ts(28,15): error TS2304: C
4242
// Contextually type the default arg using the function's contextual type
4343
var f5: (a: (s: string) => any) => void = function (a = s => <number>s) { };
4444
~~~~~~~~~
45-
!!! error TS2352: Neither type 'string' nor type 'number' is assignable to the other.
45+
!!! error TS2352: Type 'string' cannot be converted to type 'number'.
4646

4747
// Instantiated module
4848
module T { }

0 commit comments

Comments
 (0)