Merge pull request #3856 from Microsoft/widenTuples

Widening for tuples
This commit is contained in:
Jason Freeman
2015-07-14 14:17:37 -07:00
27 changed files with 290 additions and 32 deletions
+30 -13
View File
@@ -3943,7 +3943,7 @@ namespace ts {
let id = getTypeListId(elementTypes);
let type = tupleTypes[id];
if (!type) {
type = tupleTypes[id] = <TupleType>createObjectType(TypeFlags.Tuple);
type = tupleTypes[id] = <TupleType>createObjectType(TypeFlags.Tuple | getWideningFlagsOfTypes(elementTypes));
type.elementTypes = elementTypes;
}
return type;
@@ -5299,8 +5299,8 @@ namespace ts {
* Check if a Type was written as a tuple type literal.
* Prefer using isTupleLikeType() unless the use of `elementTypes` is required.
*/
function isTupleType(type: Type): boolean {
return (type.flags & TypeFlags.Tuple) && !!(<TupleType>type).elementTypes;
function isTupleType(type: Type): type is TupleType {
return !!(type.flags & TypeFlags.Tuple);
}
function getWidenedTypeOfObjectLiteral(type: Type): Type {
@@ -5341,26 +5341,45 @@ namespace ts {
if (isArrayType(type)) {
return createArrayType(getWidenedType((<TypeReference>type).typeArguments[0]));
}
if (isTupleType(type)) {
return createTupleType(map(type.elementTypes, getWidenedType));
}
}
return type;
}
/**
* Reports implicit any errors that occur as a result of widening 'null' and 'undefined'
* to 'any'. A call to reportWideningErrorsInType is normally accompanied by a call to
* getWidenedType. But in some cases getWidenedType is called without reporting errors
* (type argument inference is an example).
*
* The return value indicates whether an error was in fact reported. The particular circumstances
* are on a best effort basis. Currently, if the null or undefined that causes widening is inside
* an object literal property (arbitrarily deeply), this function reports an error. If no error is
* reported, reportImplicitAnyError is a suitable fallback to report a general error.
*/
function reportWideningErrorsInType(type: Type): boolean {
let errorReported = false;
if (type.flags & TypeFlags.Union) {
let errorReported = false;
forEach((<UnionType>type).types, t => {
for (let t of (<UnionType>type).types) {
if (reportWideningErrorsInType(t)) {
errorReported = true;
}
});
return errorReported;
}
}
if (isArrayType(type)) {
return reportWideningErrorsInType((<TypeReference>type).typeArguments[0]);
}
if (isTupleType(type)) {
for (let t of type.elementTypes) {
if (reportWideningErrorsInType(t)) {
errorReported = true;
}
}
}
if (type.flags & TypeFlags.ObjectLiteral) {
let errorReported = false;
forEach(getPropertiesOfObjectType(type), p => {
for (let p of getPropertiesOfObjectType(type)) {
let t = getTypeOfSymbol(p);
if (t.flags & TypeFlags.ContainsUndefinedOrNull) {
if (!reportWideningErrorsInType(t)) {
@@ -5368,10 +5387,9 @@ namespace ts {
}
errorReported = true;
}
});
return errorReported;
}
}
return false;
return errorReported;
}
function reportImplicitAnyError(declaration: Declaration, type: Type) {
@@ -6964,7 +6982,6 @@ namespace ts {
}
}
function checkJsxSelfClosingElement(node: JsxSelfClosingElement) {
checkJsxOpeningLikeElement(node);
return jsxElementType || anyType;
@@ -1,16 +1,8 @@
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]'.
Types of property '0' are incompatible.
Type 'string' is not assignable to type 'undefined'.
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]]]'.
Types of property '0' are incompatible.
Type '[string]' is not assignable to type '[undefined]'.
Types of property '0' are incompatible.
Type 'string' is not assignable to type 'undefined'.
tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES5.ts(62,10): error TS2393: Duplicate function implementation.
tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES5.ts(63,10): error TS2393: Duplicate function implementation.
==== tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES5.ts (4 errors) ====
==== tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES5.ts (2 errors) ====
// A parameter declaration may specify either an identifier or a binding pattern.
// The identifiers specified in parameter declarations and binding patterns
// in a parameter list must be unique within that parameter list.
@@ -43,17 +35,7 @@ tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration1ES5.
b2("string", { x: 200, y: "string" });
b2("string", { x: 200, y: true });
b6(["string", 1, 2]); // Shouldn't be an error
~~~~~~~~~~~~~~~~
!!! error TS2345: Argument of type '[string, number, number]' is not assignable to parameter of type '[undefined, null, undefined]'.
!!! error TS2345: Types of property '0' are incompatible.
!!! error TS2345: Type 'string' is not assignable to type 'undefined'.
b7([["string"], 1, [[true, false]]]); // Shouldn't be an error
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2345: Argument of type '[[string], number, [[boolean, boolean]]]' is not assignable to parameter of type '[[undefined], undefined, [[undefined, undefined]]]'.
!!! error TS2345: Types of property '0' are incompatible.
!!! error TS2345: Type '[string]' is not assignable to type '[undefined]'.
!!! error TS2345: Types of property '0' are incompatible.
!!! error TS2345: Type 'string' is not assignable to type 'undefined'.
// If the declaration specifies a binding pattern, the parameter type is the implied type of that binding pattern (section 5.1.3)
@@ -0,0 +1,9 @@
//// [wideningTuples1.ts]
declare function foo<T extends [any]>(x: T): T;
var y = foo([undefined]);
y = [""];
//// [wideningTuples1.js]
var y = foo([undefined]);
y = [""];
@@ -0,0 +1,16 @@
=== tests/cases/conformance/types/tuple/wideningTuples1.ts ===
declare function foo<T extends [any]>(x: T): T;
>foo : Symbol(foo, Decl(wideningTuples1.ts, 0, 0))
>T : Symbol(T, Decl(wideningTuples1.ts, 0, 21))
>x : Symbol(x, Decl(wideningTuples1.ts, 0, 38))
>T : Symbol(T, Decl(wideningTuples1.ts, 0, 21))
>T : Symbol(T, Decl(wideningTuples1.ts, 0, 21))
var y = foo([undefined]);
>y : Symbol(y, Decl(wideningTuples1.ts, 2, 3))
>foo : Symbol(foo, Decl(wideningTuples1.ts, 0, 0))
>undefined : Symbol(undefined)
y = [""];
>y : Symbol(y, Decl(wideningTuples1.ts, 2, 3))
@@ -0,0 +1,21 @@
=== tests/cases/conformance/types/tuple/wideningTuples1.ts ===
declare function foo<T extends [any]>(x: T): T;
>foo : <T extends [any]>(x: T) => T
>T : T
>x : T
>T : T
>T : T
var y = foo([undefined]);
>y : [any]
>foo([undefined]) : [any]
>foo : <T extends [any]>(x: T) => T
>[undefined] : [undefined]
>undefined : undefined
y = [""];
>y = [""] : [string]
>y : [any]
>[""] : [string]
>"" : string
@@ -0,0 +1,13 @@
//// [wideningTuples2.ts]
var foo: () => [any] = function bar() {
let intermediate = bar();
intermediate = [""];
return [undefined];
};
//// [wideningTuples2.js]
var foo = function bar() {
var intermediate = bar();
intermediate = [""];
return [undefined];
};
@@ -0,0 +1,16 @@
=== tests/cases/conformance/types/tuple/wideningTuples2.ts ===
var foo: () => [any] = function bar() {
>foo : Symbol(foo, Decl(wideningTuples2.ts, 0, 3))
>bar : Symbol(bar, Decl(wideningTuples2.ts, 0, 22))
let intermediate = bar();
>intermediate : Symbol(intermediate, Decl(wideningTuples2.ts, 1, 7))
>bar : Symbol(bar, Decl(wideningTuples2.ts, 0, 22))
intermediate = [""];
>intermediate : Symbol(intermediate, Decl(wideningTuples2.ts, 1, 7))
return [undefined];
>undefined : Symbol(undefined)
};
@@ -0,0 +1,22 @@
=== tests/cases/conformance/types/tuple/wideningTuples2.ts ===
var foo: () => [any] = function bar() {
>foo : () => [any]
>function bar() { let intermediate = bar(); intermediate = [""]; return [undefined];} : () => [any]
>bar : () => [any]
let intermediate = bar();
>intermediate : [any]
>bar() : [any]
>bar : () => [any]
intermediate = [""];
>intermediate = [""] : [string]
>intermediate : [any]
>[""] : [string]
>"" : string
return [undefined];
>[undefined] : [undefined]
>undefined : undefined
};
@@ -0,0 +1,9 @@
tests/cases/conformance/types/tuple/wideningTuples3.ts(3,5): error TS7005: Variable 'b' implicitly has an '[any, any]' type.
==== tests/cases/conformance/types/tuple/wideningTuples3.ts (1 errors) ====
var a: [any];
var b = a = [undefined, null];
~
!!! error TS7005: Variable 'b' implicitly has an '[any, any]' type.
@@ -0,0 +1,8 @@
//// [wideningTuples3.ts]
var a: [any];
var b = a = [undefined, null];
//// [wideningTuples3.js]
var a;
var b = a = [undefined, null];
@@ -0,0 +1,10 @@
//// [wideningTuples4.ts]
var a: [any];
var b = a = [undefined, null];
b = ["", ""];
//// [wideningTuples4.js]
var a;
var b = a = [undefined, null];
b = ["", ""];
@@ -0,0 +1,12 @@
=== tests/cases/conformance/types/tuple/wideningTuples4.ts ===
var a: [any];
>a : Symbol(a, Decl(wideningTuples4.ts, 0, 3))
var b = a = [undefined, null];
>b : Symbol(b, Decl(wideningTuples4.ts, 2, 3))
>a : Symbol(a, Decl(wideningTuples4.ts, 0, 3))
>undefined : Symbol(undefined)
b = ["", ""];
>b : Symbol(b, Decl(wideningTuples4.ts, 2, 3))
@@ -0,0 +1,19 @@
=== tests/cases/conformance/types/tuple/wideningTuples4.ts ===
var a: [any];
>a : [any]
var b = a = [undefined, null];
>b : [any, any]
>a = [undefined, null] : [undefined, null]
>a : [any]
>[undefined, null] : [undefined, null]
>undefined : undefined
>null : null
b = ["", ""];
>b = ["", ""] : [string, string]
>b : [any, any]
>["", ""] : [string, string]
>"" : string
>"" : string
@@ -0,0 +1,10 @@
tests/cases/conformance/types/tuple/wideningTuples5.ts(1,6): error TS7005: Variable 'a' implicitly has an 'any' type.
tests/cases/conformance/types/tuple/wideningTuples5.ts(1,9): error TS7005: Variable 'b' implicitly has an 'any' type.
==== tests/cases/conformance/types/tuple/wideningTuples5.ts (2 errors) ====
var [a, b] = [undefined, null];
~
!!! error TS7005: Variable 'a' implicitly has an 'any' type.
~
!!! error TS7005: Variable 'b' implicitly has an 'any' type.
@@ -0,0 +1,5 @@
//// [wideningTuples5.ts]
var [a, b] = [undefined, null];
//// [wideningTuples5.js]
var _a = [undefined, null], a = _a[0], b = _a[1];
@@ -0,0 +1,9 @@
//// [wideningTuples6.ts]
var [a, b] = [undefined, null];
a = "";
b = "";
//// [wideningTuples6.js]
var _a = [undefined, null], a = _a[0], b = _a[1];
a = "";
b = "";
@@ -0,0 +1,12 @@
=== tests/cases/conformance/types/tuple/wideningTuples6.ts ===
var [a, b] = [undefined, null];
>a : Symbol(a, Decl(wideningTuples6.ts, 0, 5))
>b : Symbol(b, Decl(wideningTuples6.ts, 0, 7))
>undefined : Symbol(undefined)
a = "";
>a : Symbol(a, Decl(wideningTuples6.ts, 0, 5))
b = "";
>b : Symbol(b, Decl(wideningTuples6.ts, 0, 7))
@@ -0,0 +1,18 @@
=== tests/cases/conformance/types/tuple/wideningTuples6.ts ===
var [a, b] = [undefined, null];
>a : any
>b : any
>[undefined, null] : [undefined, null]
>undefined : undefined
>null : null
a = "";
>a = "" : string
>a : any
>"" : string
b = "";
>b = "" : string
>b : any
>"" : string
@@ -0,0 +1,10 @@
tests/cases/conformance/types/tuple/wideningTuples7.ts(1,20): error TS7010: 'bar', which lacks return-type annotation, implicitly has an '[any]' return type.
==== tests/cases/conformance/types/tuple/wideningTuples7.ts (1 errors) ====
var foo = function bar() {
~~~
!!! error TS7010: 'bar', which lacks return-type annotation, implicitly has an '[any]' return type.
let intermediate: [string];
return intermediate = [undefined];
};
@@ -0,0 +1,11 @@
//// [wideningTuples7.ts]
var foo = function bar() {
let intermediate: [string];
return intermediate = [undefined];
};
//// [wideningTuples7.js]
var foo = function bar() {
var intermediate;
return intermediate = [undefined];
};
@@ -0,0 +1,5 @@
//@noImplicitAny: true
declare function foo<T extends [any]>(x: T): T;
var y = foo([undefined]);
y = [""];
@@ -0,0 +1,6 @@
//@noImplicitAny: true
var foo: () => [any] = function bar() {
let intermediate = bar();
intermediate = [""];
return [undefined];
};
@@ -0,0 +1,4 @@
//@noImplicitAny: true
var a: [any];
var b = a = [undefined, null];
@@ -0,0 +1,4 @@
var a: [any];
var b = a = [undefined, null];
b = ["", ""];
@@ -0,0 +1,2 @@
//@noImplicitAny: true
var [a, b] = [undefined, null];
@@ -0,0 +1,3 @@
var [a, b] = [undefined, null];
a = "";
b = "";
@@ -0,0 +1,5 @@
//@noImplicitAny: true
var foo = function bar() {
let intermediate: [string];
return intermediate = [undefined];
};