Skip to content

Commit

Permalink
Merge pull request #5517 from Microsoft/comparableRelation
Browse files Browse the repository at this point in the history
Introduce the "comparable" relation
  • Loading branch information
DanielRosenwasser committed Mar 30, 2016
2 parents 0556b15 + 3cc64cb commit 9705c89
Show file tree
Hide file tree
Showing 43 changed files with 780 additions and 99 deletions.
49 changes: 34 additions & 15 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5397,6 +5397,10 @@ namespace ts {
return checkTypeAssignableTo(source, target, /*errorNode*/ undefined);
}

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

/**
* This is *not* a bi-directional relationship.
* If one needs to check both directions for comparability, use a second call to this function or 'isTypeComparableTo'.
*/
function checkTypeComparableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean {
return checkTypeRelatedTo(source, target, comparableRelation, errorNode, headMessage, containingMessageChain);
}
Expand Down Expand Up @@ -5619,7 +5627,14 @@ namespace ts {
sourceType = typeToString(source, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType);
targetType = typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType);
}
reportError(message || Diagnostics.Type_0_is_not_assignable_to_type_1, sourceType, targetType);

if (!message) {
message = relation === comparableRelation ?
Diagnostics.Type_0_is_not_comparable_to_type_1 :
Diagnostics.Type_0_is_not_assignable_to_type_1;
}

reportError(message, sourceType, targetType);
}

// Compare two types and return
Expand Down Expand Up @@ -5648,10 +5663,12 @@ namespace ts {
}
}
if (source.flags & TypeFlags.StringLiteral && target === stringType) return Ternary.True;

if (relation === assignableRelation || relation === comparableRelation) {
if (isTypeAny(source)) return Ternary.True;
if (source === numberType && target.flags & TypeFlags.Enum) return Ternary.True;
}

if (source.flags & TypeFlags.Boolean && target.flags & TypeFlags.Boolean) {
return Ternary.True;
}
Expand All @@ -5674,26 +5691,28 @@ namespace ts {

const saveErrorInfo = errorInfo;

// Note that the "each" checks must precede the "some" checks to produce the correct results
// Note that these checks are specifically ordered to produce correct results.
if (source.flags & TypeFlags.Union) {
if (relation === comparableRelation) {
if (result = someTypeRelatedToType(<UnionType>source, target, reportErrors)) {
return result;
}
result = someTypeRelatedToType(source as UnionType, target, reportErrors);
}
else {
if (result = eachTypeRelatedToType(<UnionType>source, target, reportErrors)) {
return result;
}
result = eachTypeRelatedToType(source as UnionType, target, reportErrors);
}

if (result) {
return result;
}
}
else if (target.flags & TypeFlags.Intersection) {
if (result = typeRelatedToEachType(source, <IntersectionType>target, reportErrors)) {
result = typeRelatedToEachType(source, target as IntersectionType, reportErrors);

if (result) {
return result;
}
}
else {
// It is necessary to try "some" checks on both sides because there may be nested "each" checks
// It is necessary to try these "some" checks on both sides because there may be nested "each" checks
// on either side that need to be prioritized. For example, A | B = (A | B) & (C | D) or
// A & B = (A & B) | (C & D).
if (source.flags & TypeFlags.Intersection) {
Expand Down Expand Up @@ -5775,8 +5794,8 @@ namespace ts {
}
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union ||
source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
if (result = eachTypeRelatedToSomeType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target)) {
if (result &= eachTypeRelatedToSomeType(<UnionOrIntersectionType>target, <UnionOrIntersectionType>source)) {
if (result = eachTypeRelatedToSomeType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target, /*reportErrors*/ false)) {
if (result &= eachTypeRelatedToSomeType(<UnionOrIntersectionType>target, <UnionOrIntersectionType>source, /*reportErrors*/ false)) {
return result;
}
}
Expand Down Expand Up @@ -5827,7 +5846,7 @@ namespace ts {
return false;
}

function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType): Ternary {
function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType, reportErrors: boolean): Ternary {
let result = Ternary.True;
const sourceTypes = source.types;
for (const sourceType of sourceTypes) {
Expand Down Expand Up @@ -11015,7 +11034,7 @@ namespace ts {
if (produceDiagnostics && targetType !== unknownType) {
const widenedType = getWidenedType(exprType);
if (!isTypeComparableTo(targetType, widenedType)) {
checkTypeComparableTo(exprType, targetType, node, Diagnostics.Neither_type_0_nor_type_1_is_assignable_to_the_other);
checkTypeComparableTo(exprType, targetType, node, Diagnostics.Type_0_cannot_be_converted_to_type_1);
}
}
return targetType;
Expand Down Expand Up @@ -14458,7 +14477,7 @@ namespace ts {

if (produceDiagnostics && clause.kind === SyntaxKind.CaseClause) {
const caseClause = <CaseClause>clause;
// TypeScript 1.0 spec (April 2014):5.9
// TypeScript 1.0 spec (April 2014): 5.9
// In a 'switch' statement, each 'case' expression must be of a type that is comparable
// to or from the type of the 'switch' expression.
const caseType = checkExpression(caseClause.expression);
Expand Down
6 changes: 5 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,7 @@
"category": "Error",
"code": 2351
},
"Neither type '{0}' nor type '{1}' is assignable to the other.": {
"Type '{0}' cannot be converted to type '{1}'.": {
"category": "Error",
"code": 2352
},
Expand Down Expand Up @@ -1875,6 +1875,10 @@
"category": "Error",
"code": 2677
},
"Type '{0}' is not comparable to type '{1}'.": {
"category": "Error",
"code": 2678
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
"code": 4000
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/arrayCast.errors.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
tests/cases/compiler/arrayCast.ts(3,23): error TS2352: Neither type '{ foo: string; }[]' nor type '{ id: number; }[]' is assignable to the other.
Type '{ foo: string; }' is not assignable to type '{ id: number; }'.
tests/cases/compiler/arrayCast.ts(3,23): error TS2352: Type '{ foo: string; }[]' cannot be converted to type '{ id: number; }[]'.
Type '{ foo: string; }' is not comparable to type '{ id: number; }'.
Object literal may only specify known properties, and 'foo' does not exist in type '{ id: number; }'.


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

// Should succeed, as the {} element causes the type of the array to be {}[]
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/asOperator2.errors.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
tests/cases/conformance/expressions/asOperator/asOperator2.ts(1,9): error TS2352: Neither type 'number' nor type 'string' is assignable to the other.
tests/cases/conformance/expressions/asOperator/asOperator2.ts(1,9): error TS2352: Type 'number' cannot be converted to type 'string'.


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

8 changes: 4 additions & 4 deletions tests/baselines/reference/asOperatorContextualType.errors.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
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.
Type 'number' is not assignable to type 'string'.
tests/cases/conformance/expressions/asOperator/asOperatorContextualType.ts(2,9): error TS2352: Type '(v: number) => number' cannot be converted to type '(x: number) => string'.
Type 'number' is not comparable to type 'string'.


==== tests/cases/conformance/expressions/asOperator/asOperatorContextualType.ts (1 errors) ====
// should error
var x = (v => v) as (x: number) => string;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2352: Neither type '(v: number) => number' nor type '(x: number) => string' is assignable to the other.
!!! error TS2352: Type 'number' is not assignable to type 'string'.
!!! error TS2352: Type '(v: number) => number' cannot be converted to type '(x: number) => string'.
!!! error TS2352: Type 'number' is not comparable to type 'string'.
4 changes: 2 additions & 2 deletions tests/baselines/reference/asOperatorNames.errors.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
tests/cases/conformance/expressions/asOperator/asOperatorNames.ts(2,9): error TS2352: Neither type 'number' nor type 'string' is assignable to the other.
tests/cases/conformance/expressions/asOperator/asOperatorNames.ts(2,9): error TS2352: Type 'number' cannot be converted to type 'string'.


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

16 changes: 8 additions & 8 deletions tests/baselines/reference/castingTuple.errors.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
tests/cases/conformance/types/tuple/castingTuple.ts(28,10): error TS2352: Neither type '[number, string]' nor type '[number, number]' is assignable to the other.
tests/cases/conformance/types/tuple/castingTuple.ts(28,10): error TS2352: Type '[number, string]' cannot be converted to type '[number, number]'.
Types of property '1' are incompatible.
Type 'string' is not assignable to type 'number'.
tests/cases/conformance/types/tuple/castingTuple.ts(29,10): error TS2352: Neither type '[C, D]' nor type '[A, I]' is assignable to the other.
Type 'string' is not comparable to type 'number'.
tests/cases/conformance/types/tuple/castingTuple.ts(29,10): error TS2352: Type '[C, D]' cannot be converted to type '[A, I]'.
Types of property '0' are incompatible.
Type 'C' is not assignable to type 'A'.
Type 'C' is not comparable to type 'A'.
Property 'a' is missing in type 'C'.
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[]'.
tests/cases/conformance/types/tuple/castingTuple.ts(31,1): error TS2304: Cannot find name 't4'.
Expand Down Expand Up @@ -39,14 +39,14 @@ tests/cases/conformance/types/tuple/castingTuple.ts(31,1): error TS2304: Cannot
// error
var t3 = <[number, number]>numStrTuple;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2352: Neither type '[number, string]' nor type '[number, number]' is assignable to the other.
!!! error TS2352: Type '[number, string]' cannot be converted to type '[number, number]'.
!!! error TS2352: Types of property '1' are incompatible.
!!! error TS2352: Type 'string' is not assignable to type 'number'.
!!! error TS2352: Type 'string' is not comparable to type 'number'.
var t9 = <[A, I]>classCDTuple;
~~~~~~~~~~~~~~~~~~~~
!!! error TS2352: Neither type '[C, D]' nor type '[A, I]' is assignable to the other.
!!! error TS2352: Type '[C, D]' cannot be converted to type '[A, I]'.
!!! error TS2352: Types of property '0' are incompatible.
!!! error TS2352: Type 'C' is not assignable to type 'A'.
!!! error TS2352: Type 'C' is not comparable to type 'A'.
!!! error TS2352: Property 'a' is missing in type 'C'.
var array1 = <number[]>numStrTuple;
~~~~~~
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/contextualTyping39.errors.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
tests/cases/compiler/contextualTyping39.ts(1,11): error TS2352: Neither type '() => string' nor type '() => number' is assignable to the other.
Type 'string' is not assignable to type 'number'.
tests/cases/compiler/contextualTyping39.ts(1,11): error TS2352: Type '() => string' cannot be converted to type '() => number'.
Type 'string' is not comparable to type 'number'.


==== tests/cases/compiler/contextualTyping39.ts (1 errors) ====
var foo = <{ (): number; }> function() { return "err"; };
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2352: Neither type '() => string' nor type '() => number' is assignable to the other.
!!! error TS2352: Type 'string' is not assignable to type 'number'.
!!! error TS2352: Type '() => string' cannot be converted to type '() => number'.
!!! error TS2352: Type 'string' is not comparable to type 'number'.
8 changes: 4 additions & 4 deletions tests/baselines/reference/contextualTyping41.errors.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
tests/cases/compiler/contextualTyping41.ts(1,11): error TS2352: Neither type '() => string' nor type '{ (): number; (i: number): number; }' is assignable to the other.
Type 'string' is not assignable to type 'number'.
tests/cases/compiler/contextualTyping41.ts(1,11): error TS2352: Type '() => string' cannot be converted to type '{ (): number; (i: number): number; }'.
Type 'string' is not comparable to type 'number'.


==== tests/cases/compiler/contextualTyping41.ts (1 errors) ====
var foo = <{():number; (i:number):number; }> (function(){return "err";});
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2352: Neither type '() => string' nor type '{ (): number; (i: number): number; }' is assignable to the other.
!!! error TS2352: Type 'string' is not assignable to type 'number'.
!!! error TS2352: Type '() => string' cannot be converted to type '{ (): number; (i: number): number; }'.
!!! error TS2352: Type 'string' is not comparable to type 'number'.
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ tests/cases/compiler/defaultArgsInFunctionExpressions.ts(4,19): error TS2345: Ar
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(5,1): error TS2322: Type 'number' is not assignable to type 'string'.
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(8,20): error TS2322: Type 'number' is not assignable to type 'string'.
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(11,1): error TS2322: Type 'string' is not assignable to type 'number'.
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(14,51): error TS2352: Neither type 'string' nor type 'number' is assignable to the other.
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(14,51): error TS2352: Type 'string' cannot be converted to type 'number'.
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(17,41): error TS2322: Type 'string' is not assignable to type 'number'.
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(20,62): error TS2352: Neither type 'string' nor type 'number' is assignable to the other.
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(20,62): error TS2352: Type 'string' cannot be converted to type 'number'.
tests/cases/compiler/defaultArgsInFunctionExpressions.ts(28,15): error TS2304: Cannot find name 'T'.


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

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

// Instantiated module
module T { }
Expand Down
Loading

0 comments on commit 9705c89

Please sign in to comment.