Skip to content

Commit 742b2ad

Browse files
committed
Merge pull request microsoft#3856 from Microsoft/widenTuples
Widening for tuples
2 parents f344ec1 + 6fc08ea commit 742b2ad

27 files changed

+290
-32
lines changed

src/compiler/checker.ts

+30-13
Original file line numberDiff line numberDiff line change
@@ -3943,7 +3943,7 @@ namespace ts {
39433943
let id = getTypeListId(elementTypes);
39443944
let type = tupleTypes[id];
39453945
if (!type) {
3946-
type = tupleTypes[id] = <TupleType>createObjectType(TypeFlags.Tuple);
3946+
type = tupleTypes[id] = <TupleType>createObjectType(TypeFlags.Tuple | getWideningFlagsOfTypes(elementTypes));
39473947
type.elementTypes = elementTypes;
39483948
}
39493949
return type;
@@ -5299,8 +5299,8 @@ namespace ts {
52995299
* Check if a Type was written as a tuple type literal.
53005300
* Prefer using isTupleLikeType() unless the use of `elementTypes` is required.
53015301
*/
5302-
function isTupleType(type: Type): boolean {
5303-
return (type.flags & TypeFlags.Tuple) && !!(<TupleType>type).elementTypes;
5302+
function isTupleType(type: Type): type is TupleType {
5303+
return !!(type.flags & TypeFlags.Tuple);
53045304
}
53055305

53065306
function getWidenedTypeOfObjectLiteral(type: Type): Type {
@@ -5341,37 +5341,55 @@ namespace ts {
53415341
if (isArrayType(type)) {
53425342
return createArrayType(getWidenedType((<TypeReference>type).typeArguments[0]));
53435343
}
5344+
if (isTupleType(type)) {
5345+
return createTupleType(map(type.elementTypes, getWidenedType));
5346+
}
53445347
}
53455348
return type;
53465349
}
53475350

5351+
/**
5352+
* Reports implicit any errors that occur as a result of widening 'null' and 'undefined'
5353+
* to 'any'. A call to reportWideningErrorsInType is normally accompanied by a call to
5354+
* getWidenedType. But in some cases getWidenedType is called without reporting errors
5355+
* (type argument inference is an example).
5356+
*
5357+
* The return value indicates whether an error was in fact reported. The particular circumstances
5358+
* are on a best effort basis. Currently, if the null or undefined that causes widening is inside
5359+
* an object literal property (arbitrarily deeply), this function reports an error. If no error is
5360+
* reported, reportImplicitAnyError is a suitable fallback to report a general error.
5361+
*/
53485362
function reportWideningErrorsInType(type: Type): boolean {
5363+
let errorReported = false;
53495364
if (type.flags & TypeFlags.Union) {
5350-
let errorReported = false;
5351-
forEach((<UnionType>type).types, t => {
5365+
for (let t of (<UnionType>type).types) {
53525366
if (reportWideningErrorsInType(t)) {
53535367
errorReported = true;
53545368
}
5355-
});
5356-
return errorReported;
5369+
}
53575370
}
53585371
if (isArrayType(type)) {
53595372
return reportWideningErrorsInType((<TypeReference>type).typeArguments[0]);
53605373
}
5374+
if (isTupleType(type)) {
5375+
for (let t of type.elementTypes) {
5376+
if (reportWideningErrorsInType(t)) {
5377+
errorReported = true;
5378+
}
5379+
}
5380+
}
53615381
if (type.flags & TypeFlags.ObjectLiteral) {
5362-
let errorReported = false;
5363-
forEach(getPropertiesOfObjectType(type), p => {
5382+
for (let p of getPropertiesOfObjectType(type)) {
53645383
let t = getTypeOfSymbol(p);
53655384
if (t.flags & TypeFlags.ContainsUndefinedOrNull) {
53665385
if (!reportWideningErrorsInType(t)) {
53675386
error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, p.name, typeToString(getWidenedType(t)));
53685387
}
53695388
errorReported = true;
53705389
}
5371-
});
5372-
return errorReported;
5390+
}
53735391
}
5374-
return false;
5392+
return errorReported;
53755393
}
53765394

53775395
function reportImplicitAnyError(declaration: Declaration, type: Type) {
@@ -6964,7 +6982,6 @@ namespace ts {
69646982
}
69656983
}
69666984

6967-
69686985
function checkJsxSelfClosingElement(node: JsxSelfClosingElement) {
69696986
checkJsxOpeningLikeElement(node);
69706987
return jsxElementType || anyType;

tests/baselines/reference/destructuringParameterDeclaration1ES5.errors.txt

+1-19
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
1-
tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES5.ts(32,4): error TS2345: Argument of type '[string, number, number]' is not assignable to parameter of type '[undefined, null, undefined]'.
2-
Types of property '0' are incompatible.
3-
Type 'string' is not assignable to type 'undefined'.
4-
tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES5.ts(33,4): error TS2345: Argument of type '[[string], number, [[boolean, boolean]]]' is not assignable to parameter of type '[[undefined], undefined, [[undefined, undefined]]]'.
5-
Types of property '0' are incompatible.
6-
Type '[string]' is not assignable to type '[undefined]'.
7-
Types of property '0' are incompatible.
8-
Type 'string' is not assignable to type 'undefined'.
91
tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES5.ts(62,10): error TS2393: Duplicate function implementation.
102
tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES5.ts(63,10): error TS2393: Duplicate function implementation.
113

124

13-
==== tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES5.ts (4 errors) ====
5+
==== tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES5.ts (2 errors) ====
146
// A parameter declaration may specify either an identifier or a binding pattern.
157
// The identifiers specified in parameter declarations and binding patterns
168
// in a parameter list must be unique within that parameter list.
@@ -43,17 +35,7 @@ tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES5.
4335
b2("string", { x: 200, y: "string" });
4436
b2("string", { x: 200, y: true });
4537
b6(["string", 1, 2]); // Shouldn't be an error
46-
~~~~~~~~~~~~~~~~
47-
!!! error TS2345: Argument of type '[string, number, number]' is not assignable to parameter of type '[undefined, null, undefined]'.
48-
!!! error TS2345: Types of property '0' are incompatible.
49-
!!! error TS2345: Type 'string' is not assignable to type 'undefined'.
5038
b7([["string"], 1, [[true, false]]]); // Shouldn't be an error
51-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
52-
!!! error TS2345: Argument of type '[[string], number, [[boolean, boolean]]]' is not assignable to parameter of type '[[undefined], undefined, [[undefined, undefined]]]'.
53-
!!! error TS2345: Types of property '0' are incompatible.
54-
!!! error TS2345: Type '[string]' is not assignable to type '[undefined]'.
55-
!!! error TS2345: Types of property '0' are incompatible.
56-
!!! error TS2345: Type 'string' is not assignable to type 'undefined'.
5739

5840

5941
// If the declaration specifies a binding pattern, the parameter type is the implied type of that binding pattern (section 5.1.3)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//// [wideningTuples1.ts]
2+
declare function foo<T extends [any]>(x: T): T;
3+
4+
var y = foo([undefined]);
5+
y = [""];
6+
7+
//// [wideningTuples1.js]
8+
var y = foo([undefined]);
9+
y = [""];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
=== tests/cases/conformance/types/tuple/wideningTuples1.ts ===
2+
declare function foo<T extends [any]>(x: T): T;
3+
>foo : Symbol(foo, Decl(wideningTuples1.ts, 0, 0))
4+
>T : Symbol(T, Decl(wideningTuples1.ts, 0, 21))
5+
>x : Symbol(x, Decl(wideningTuples1.ts, 0, 38))
6+
>T : Symbol(T, Decl(wideningTuples1.ts, 0, 21))
7+
>T : Symbol(T, Decl(wideningTuples1.ts, 0, 21))
8+
9+
var y = foo([undefined]);
10+
>y : Symbol(y, Decl(wideningTuples1.ts, 2, 3))
11+
>foo : Symbol(foo, Decl(wideningTuples1.ts, 0, 0))
12+
>undefined : Symbol(undefined)
13+
14+
y = [""];
15+
>y : Symbol(y, Decl(wideningTuples1.ts, 2, 3))
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
=== tests/cases/conformance/types/tuple/wideningTuples1.ts ===
2+
declare function foo<T extends [any]>(x: T): T;
3+
>foo : <T extends [any]>(x: T) => T
4+
>T : T
5+
>x : T
6+
>T : T
7+
>T : T
8+
9+
var y = foo([undefined]);
10+
>y : [any]
11+
>foo([undefined]) : [any]
12+
>foo : <T extends [any]>(x: T) => T
13+
>[undefined] : [undefined]
14+
>undefined : undefined
15+
16+
y = [""];
17+
>y = [""] : [string]
18+
>y : [any]
19+
>[""] : [string]
20+
>"" : string
21+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//// [wideningTuples2.ts]
2+
var foo: () => [any] = function bar() {
3+
let intermediate = bar();
4+
intermediate = [""];
5+
return [undefined];
6+
};
7+
8+
//// [wideningTuples2.js]
9+
var foo = function bar() {
10+
var intermediate = bar();
11+
intermediate = [""];
12+
return [undefined];
13+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
=== tests/cases/conformance/types/tuple/wideningTuples2.ts ===
2+
var foo: () => [any] = function bar() {
3+
>foo : Symbol(foo, Decl(wideningTuples2.ts, 0, 3))
4+
>bar : Symbol(bar, Decl(wideningTuples2.ts, 0, 22))
5+
6+
let intermediate = bar();
7+
>intermediate : Symbol(intermediate, Decl(wideningTuples2.ts, 1, 7))
8+
>bar : Symbol(bar, Decl(wideningTuples2.ts, 0, 22))
9+
10+
intermediate = [""];
11+
>intermediate : Symbol(intermediate, Decl(wideningTuples2.ts, 1, 7))
12+
13+
return [undefined];
14+
>undefined : Symbol(undefined)
15+
16+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/conformance/types/tuple/wideningTuples2.ts ===
2+
var foo: () => [any] = function bar() {
3+
>foo : () => [any]
4+
>function bar() { let intermediate = bar(); intermediate = [""]; return [undefined];} : () => [any]
5+
>bar : () => [any]
6+
7+
let intermediate = bar();
8+
>intermediate : [any]
9+
>bar() : [any]
10+
>bar : () => [any]
11+
12+
intermediate = [""];
13+
>intermediate = [""] : [string]
14+
>intermediate : [any]
15+
>[""] : [string]
16+
>"" : string
17+
18+
return [undefined];
19+
>[undefined] : [undefined]
20+
>undefined : undefined
21+
22+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
tests/cases/conformance/types/tuple/wideningTuples3.ts(3,5): error TS7005: Variable 'b' implicitly has an '[any, any]' type.
2+
3+
4+
==== tests/cases/conformance/types/tuple/wideningTuples3.ts (1 errors) ====
5+
var a: [any];
6+
7+
var b = a = [undefined, null];
8+
~
9+
!!! error TS7005: Variable 'b' implicitly has an '[any, any]' type.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//// [wideningTuples3.ts]
2+
var a: [any];
3+
4+
var b = a = [undefined, null];
5+
6+
//// [wideningTuples3.js]
7+
var a;
8+
var b = a = [undefined, null];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//// [wideningTuples4.ts]
2+
var a: [any];
3+
4+
var b = a = [undefined, null];
5+
b = ["", ""];
6+
7+
//// [wideningTuples4.js]
8+
var a;
9+
var b = a = [undefined, null];
10+
b = ["", ""];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
=== tests/cases/conformance/types/tuple/wideningTuples4.ts ===
2+
var a: [any];
3+
>a : Symbol(a, Decl(wideningTuples4.ts, 0, 3))
4+
5+
var b = a = [undefined, null];
6+
>b : Symbol(b, Decl(wideningTuples4.ts, 2, 3))
7+
>a : Symbol(a, Decl(wideningTuples4.ts, 0, 3))
8+
>undefined : Symbol(undefined)
9+
10+
b = ["", ""];
11+
>b : Symbol(b, Decl(wideningTuples4.ts, 2, 3))
12+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
=== tests/cases/conformance/types/tuple/wideningTuples4.ts ===
2+
var a: [any];
3+
>a : [any]
4+
5+
var b = a = [undefined, null];
6+
>b : [any, any]
7+
>a = [undefined, null] : [undefined, null]
8+
>a : [any]
9+
>[undefined, null] : [undefined, null]
10+
>undefined : undefined
11+
>null : null
12+
13+
b = ["", ""];
14+
>b = ["", ""] : [string, string]
15+
>b : [any, any]
16+
>["", ""] : [string, string]
17+
>"" : string
18+
>"" : string
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
tests/cases/conformance/types/tuple/wideningTuples5.ts(1,6): error TS7005: Variable 'a' implicitly has an 'any' type.
2+
tests/cases/conformance/types/tuple/wideningTuples5.ts(1,9): error TS7005: Variable 'b' implicitly has an 'any' type.
3+
4+
5+
==== tests/cases/conformance/types/tuple/wideningTuples5.ts (2 errors) ====
6+
var [a, b] = [undefined, null];
7+
~
8+
!!! error TS7005: Variable 'a' implicitly has an 'any' type.
9+
~
10+
!!! error TS7005: Variable 'b' implicitly has an 'any' type.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//// [wideningTuples5.ts]
2+
var [a, b] = [undefined, null];
3+
4+
//// [wideningTuples5.js]
5+
var _a = [undefined, null], a = _a[0], b = _a[1];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//// [wideningTuples6.ts]
2+
var [a, b] = [undefined, null];
3+
a = "";
4+
b = "";
5+
6+
//// [wideningTuples6.js]
7+
var _a = [undefined, null], a = _a[0], b = _a[1];
8+
a = "";
9+
b = "";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
=== tests/cases/conformance/types/tuple/wideningTuples6.ts ===
2+
var [a, b] = [undefined, null];
3+
>a : Symbol(a, Decl(wideningTuples6.ts, 0, 5))
4+
>b : Symbol(b, Decl(wideningTuples6.ts, 0, 7))
5+
>undefined : Symbol(undefined)
6+
7+
a = "";
8+
>a : Symbol(a, Decl(wideningTuples6.ts, 0, 5))
9+
10+
b = "";
11+
>b : Symbol(b, Decl(wideningTuples6.ts, 0, 7))
12+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
=== tests/cases/conformance/types/tuple/wideningTuples6.ts ===
2+
var [a, b] = [undefined, null];
3+
>a : any
4+
>b : any
5+
>[undefined, null] : [undefined, null]
6+
>undefined : undefined
7+
>null : null
8+
9+
a = "";
10+
>a = "" : string
11+
>a : any
12+
>"" : string
13+
14+
b = "";
15+
>b = "" : string
16+
>b : any
17+
>"" : string
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
tests/cases/conformance/types/tuple/wideningTuples7.ts(1,20): error TS7010: 'bar', which lacks return-type annotation, implicitly has an '[any]' return type.
2+
3+
4+
==== tests/cases/conformance/types/tuple/wideningTuples7.ts (1 errors) ====
5+
var foo = function bar() {
6+
~~~
7+
!!! error TS7010: 'bar', which lacks return-type annotation, implicitly has an '[any]' return type.
8+
let intermediate: [string];
9+
return intermediate = [undefined];
10+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//// [wideningTuples7.ts]
2+
var foo = function bar() {
3+
let intermediate: [string];
4+
return intermediate = [undefined];
5+
};
6+
7+
//// [wideningTuples7.js]
8+
var foo = function bar() {
9+
var intermediate;
10+
return intermediate = [undefined];
11+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//@noImplicitAny: true
2+
declare function foo<T extends [any]>(x: T): T;
3+
4+
var y = foo([undefined]);
5+
y = [""];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//@noImplicitAny: true
2+
var foo: () => [any] = function bar() {
3+
let intermediate = bar();
4+
intermediate = [""];
5+
return [undefined];
6+
};

0 commit comments

Comments
 (0)