diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4e4fc6919a0..f8bafa7a38f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3943,7 +3943,7 @@ namespace ts { let id = getTypeListId(elementTypes); let type = tupleTypes[id]; if (!type) { - type = tupleTypes[id] = createObjectType(TypeFlags.Tuple); + type = tupleTypes[id] = 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) && !!(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((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((type).types, t => { + for (let t of (type).types) { if (reportWideningErrorsInType(t)) { errorReported = true; } - }); - return errorReported; + } } if (isArrayType(type)) { return reportWideningErrorsInType((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; diff --git a/tests/baselines/reference/destructuringParameterDeclaration1ES5.errors.txt b/tests/baselines/reference/destructuringParameterDeclaration1ES5.errors.txt index 269d2b5640d..8e51c2b6111 100644 --- a/tests/baselines/reference/destructuringParameterDeclaration1ES5.errors.txt +++ b/tests/baselines/reference/destructuringParameterDeclaration1ES5.errors.txt @@ -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) diff --git a/tests/baselines/reference/wideningTuples1.js b/tests/baselines/reference/wideningTuples1.js new file mode 100644 index 00000000000..ed39a735bfe --- /dev/null +++ b/tests/baselines/reference/wideningTuples1.js @@ -0,0 +1,9 @@ +//// [wideningTuples1.ts] +declare function foo(x: T): T; + +var y = foo([undefined]); +y = [""]; + +//// [wideningTuples1.js] +var y = foo([undefined]); +y = [""]; diff --git a/tests/baselines/reference/wideningTuples1.symbols b/tests/baselines/reference/wideningTuples1.symbols new file mode 100644 index 00000000000..819510b7319 --- /dev/null +++ b/tests/baselines/reference/wideningTuples1.symbols @@ -0,0 +1,16 @@ +=== tests/cases/conformance/types/tuple/wideningTuples1.ts === +declare function foo(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)) + diff --git a/tests/baselines/reference/wideningTuples1.types b/tests/baselines/reference/wideningTuples1.types new file mode 100644 index 00000000000..1fdd4a6cb53 --- /dev/null +++ b/tests/baselines/reference/wideningTuples1.types @@ -0,0 +1,21 @@ +=== tests/cases/conformance/types/tuple/wideningTuples1.ts === +declare function foo(x: T): T; +>foo : (x: T) => T +>T : T +>x : T +>T : T +>T : T + +var y = foo([undefined]); +>y : [any] +>foo([undefined]) : [any] +>foo : (x: T) => T +>[undefined] : [undefined] +>undefined : undefined + +y = [""]; +>y = [""] : [string] +>y : [any] +>[""] : [string] +>"" : string + diff --git a/tests/baselines/reference/wideningTuples2.js b/tests/baselines/reference/wideningTuples2.js new file mode 100644 index 00000000000..fa74e089961 --- /dev/null +++ b/tests/baselines/reference/wideningTuples2.js @@ -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]; +}; diff --git a/tests/baselines/reference/wideningTuples2.symbols b/tests/baselines/reference/wideningTuples2.symbols new file mode 100644 index 00000000000..74186045e90 --- /dev/null +++ b/tests/baselines/reference/wideningTuples2.symbols @@ -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) + +}; diff --git a/tests/baselines/reference/wideningTuples2.types b/tests/baselines/reference/wideningTuples2.types new file mode 100644 index 00000000000..c07166c53f0 --- /dev/null +++ b/tests/baselines/reference/wideningTuples2.types @@ -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 + +}; diff --git a/tests/baselines/reference/wideningTuples3.errors.txt b/tests/baselines/reference/wideningTuples3.errors.txt new file mode 100644 index 00000000000..43c7e349a0c --- /dev/null +++ b/tests/baselines/reference/wideningTuples3.errors.txt @@ -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. \ No newline at end of file diff --git a/tests/baselines/reference/wideningTuples3.js b/tests/baselines/reference/wideningTuples3.js new file mode 100644 index 00000000000..dfeaae745d4 --- /dev/null +++ b/tests/baselines/reference/wideningTuples3.js @@ -0,0 +1,8 @@ +//// [wideningTuples3.ts] +var a: [any]; + +var b = a = [undefined, null]; + +//// [wideningTuples3.js] +var a; +var b = a = [undefined, null]; diff --git a/tests/baselines/reference/wideningTuples4.js b/tests/baselines/reference/wideningTuples4.js new file mode 100644 index 00000000000..b4d16a3058b --- /dev/null +++ b/tests/baselines/reference/wideningTuples4.js @@ -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 = ["", ""]; diff --git a/tests/baselines/reference/wideningTuples4.symbols b/tests/baselines/reference/wideningTuples4.symbols new file mode 100644 index 00000000000..95e7cc3cff8 --- /dev/null +++ b/tests/baselines/reference/wideningTuples4.symbols @@ -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)) + diff --git a/tests/baselines/reference/wideningTuples4.types b/tests/baselines/reference/wideningTuples4.types new file mode 100644 index 00000000000..6d101dd1e01 --- /dev/null +++ b/tests/baselines/reference/wideningTuples4.types @@ -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 + diff --git a/tests/baselines/reference/wideningTuples5.errors.txt b/tests/baselines/reference/wideningTuples5.errors.txt new file mode 100644 index 00000000000..bfd72079f55 --- /dev/null +++ b/tests/baselines/reference/wideningTuples5.errors.txt @@ -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. \ No newline at end of file diff --git a/tests/baselines/reference/wideningTuples5.js b/tests/baselines/reference/wideningTuples5.js new file mode 100644 index 00000000000..5763fb3340f --- /dev/null +++ b/tests/baselines/reference/wideningTuples5.js @@ -0,0 +1,5 @@ +//// [wideningTuples5.ts] +var [a, b] = [undefined, null]; + +//// [wideningTuples5.js] +var _a = [undefined, null], a = _a[0], b = _a[1]; diff --git a/tests/baselines/reference/wideningTuples6.js b/tests/baselines/reference/wideningTuples6.js new file mode 100644 index 00000000000..b1d5e8ec0bd --- /dev/null +++ b/tests/baselines/reference/wideningTuples6.js @@ -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 = ""; diff --git a/tests/baselines/reference/wideningTuples6.symbols b/tests/baselines/reference/wideningTuples6.symbols new file mode 100644 index 00000000000..88de6a7b9b7 --- /dev/null +++ b/tests/baselines/reference/wideningTuples6.symbols @@ -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)) + diff --git a/tests/baselines/reference/wideningTuples6.types b/tests/baselines/reference/wideningTuples6.types new file mode 100644 index 00000000000..d75c0b49e9e --- /dev/null +++ b/tests/baselines/reference/wideningTuples6.types @@ -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 + diff --git a/tests/baselines/reference/wideningTuples7.errors.txt b/tests/baselines/reference/wideningTuples7.errors.txt new file mode 100644 index 00000000000..25fd1f58e64 --- /dev/null +++ b/tests/baselines/reference/wideningTuples7.errors.txt @@ -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]; + }; \ No newline at end of file diff --git a/tests/baselines/reference/wideningTuples7.js b/tests/baselines/reference/wideningTuples7.js new file mode 100644 index 00000000000..4d94a1909ad --- /dev/null +++ b/tests/baselines/reference/wideningTuples7.js @@ -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]; +}; diff --git a/tests/cases/conformance/types/tuple/wideningTuples1.ts b/tests/cases/conformance/types/tuple/wideningTuples1.ts new file mode 100644 index 00000000000..f4885435dcf --- /dev/null +++ b/tests/cases/conformance/types/tuple/wideningTuples1.ts @@ -0,0 +1,5 @@ +//@noImplicitAny: true +declare function foo(x: T): T; + +var y = foo([undefined]); +y = [""]; \ No newline at end of file diff --git a/tests/cases/conformance/types/tuple/wideningTuples2.ts b/tests/cases/conformance/types/tuple/wideningTuples2.ts new file mode 100644 index 00000000000..d8bf1248546 --- /dev/null +++ b/tests/cases/conformance/types/tuple/wideningTuples2.ts @@ -0,0 +1,6 @@ +//@noImplicitAny: true +var foo: () => [any] = function bar() { + let intermediate = bar(); + intermediate = [""]; + return [undefined]; +}; \ No newline at end of file diff --git a/tests/cases/conformance/types/tuple/wideningTuples3.ts b/tests/cases/conformance/types/tuple/wideningTuples3.ts new file mode 100644 index 00000000000..a6837be2a0f --- /dev/null +++ b/tests/cases/conformance/types/tuple/wideningTuples3.ts @@ -0,0 +1,4 @@ +//@noImplicitAny: true +var a: [any]; + +var b = a = [undefined, null]; \ No newline at end of file diff --git a/tests/cases/conformance/types/tuple/wideningTuples4.ts b/tests/cases/conformance/types/tuple/wideningTuples4.ts new file mode 100644 index 00000000000..550ba07f600 --- /dev/null +++ b/tests/cases/conformance/types/tuple/wideningTuples4.ts @@ -0,0 +1,4 @@ +var a: [any]; + +var b = a = [undefined, null]; +b = ["", ""]; \ No newline at end of file diff --git a/tests/cases/conformance/types/tuple/wideningTuples5.ts b/tests/cases/conformance/types/tuple/wideningTuples5.ts new file mode 100644 index 00000000000..36434c6eafe --- /dev/null +++ b/tests/cases/conformance/types/tuple/wideningTuples5.ts @@ -0,0 +1,2 @@ +//@noImplicitAny: true +var [a, b] = [undefined, null]; \ No newline at end of file diff --git a/tests/cases/conformance/types/tuple/wideningTuples6.ts b/tests/cases/conformance/types/tuple/wideningTuples6.ts new file mode 100644 index 00000000000..cac228ecb4d --- /dev/null +++ b/tests/cases/conformance/types/tuple/wideningTuples6.ts @@ -0,0 +1,3 @@ +var [a, b] = [undefined, null]; +a = ""; +b = ""; \ No newline at end of file diff --git a/tests/cases/conformance/types/tuple/wideningTuples7.ts b/tests/cases/conformance/types/tuple/wideningTuples7.ts new file mode 100644 index 00000000000..1a4d212d362 --- /dev/null +++ b/tests/cases/conformance/types/tuple/wideningTuples7.ts @@ -0,0 +1,5 @@ +//@noImplicitAny: true +var foo = function bar() { + let intermediate: [string]; + return intermediate = [undefined]; +}; \ No newline at end of file