From dfa108f4ec5e2074fe3ef0a26471fa52a5581d1e Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Tue, 14 Jul 2015 11:17:07 -0700 Subject: [PATCH 1/4] Allow tuples to be widened --- src/compiler/checker.ts | 60 +++++++++++++------ .../diagnosticInformationMap.generated.ts | 1 + src/compiler/diagnosticMessages.json | 8 +++ 3 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index da45c08ab44..5209f02847e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5299,7 +5299,7 @@ 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 { + function isTupleType(type: Type): type is TupleType { return (type.flags & TypeFlags.Tuple) && !!(type).elementTypes; } @@ -5341,37 +5341,53 @@ namespace ts { if (isArrayType(type)) { return createArrayType(getWidenedType((type).typeArguments[0])); } + if (isTupleType(type)) { + return createTupleType(map(type.elementTypes, getWidenedType)); + } } return type; } - function reportWideningErrorsInType(type: Type): boolean { + function reportWideningErrorsInType(type: Type, expression: Expression): boolean { + let errorReported = false; if (type.flags & TypeFlags.Union) { - let errorReported = false; - forEach((type).types, t => { - if (reportWideningErrorsInType(t)) { + for (let t of (type).types) { + if (reportWideningErrorsInType(t, expression)) { errorReported = true; } - }); - return errorReported; + } } if (isArrayType(type)) { - return reportWideningErrorsInType((type).typeArguments[0]); + return reportWideningErrorsInType((type).typeArguments[0], expression); + } + if (isTupleType(type)) { + let { elementTypes } = type; + for (let i = 0; i < elementTypes.length; i++) { + let t = elementTypes[i]; + if (t.flags & TypeFlags.ContainsUndefinedOrNull) { + if (reportWideningErrorsInType(t, expression)) { + errorReported = true; + } + else if (expression.kind === SyntaxKind.ArrayLiteralExpression) { + let element = (expression).elements[i]; + error(element, Diagnostics.Array_element_at_index_0_implicitly_has_an_1_type, i, typeToString(getWidenedType(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)) { + if (!reportWideningErrorsInType(t, expression)) { error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, p.name, typeToString(getWidenedType(t))); } errorReported = true; } - }); - return errorReported; + } } - return false; + return errorReported; } function reportImplicitAnyError(declaration: Declaration, type: Type) { @@ -5409,7 +5425,7 @@ namespace ts { function reportErrorsFromWidening(declaration: Declaration, type: Type) { if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsUndefinedOrNull) { // Report implicit any error within type if possible, otherwise report error on declaration - if (!reportWideningErrorsInType(type)) { + if (!reportWideningErrorsInType(type, declaration.kind === SyntaxKind.VariableDeclaration && (declaration).initializer)) { reportImplicitAnyError(declaration, type); } } @@ -6785,6 +6801,7 @@ namespace ts { let hasSpreadElement = false; let elementTypes: Type[] = []; let inDestructuringPattern = isAssignmentTarget(node); + let typeFlags: TypeFlags; for (let e of elements) { if (inDestructuringPattern && e.kind === SyntaxKind.SpreadElementExpression) { // Given the following situation: @@ -6804,21 +6821,23 @@ namespace ts { (languageVersion >= ScriptTarget.ES6 ? getElementTypeOfIterable(restArrayType, /*errorNode*/ undefined) : undefined); if (restElementType) { elementTypes.push(restElementType); + typeFlags |= restElementType.flags; } } else { let type = checkExpression(e, contextualMapper); elementTypes.push(type); + typeFlags |= type.flags; } hasSpreadElement = hasSpreadElement || e.kind === SyntaxKind.SpreadElementExpression; } if (!hasSpreadElement) { let contextualType = getContextualType(node); if (contextualType && contextualTypeIsTupleLikeType(contextualType) || inDestructuringPattern) { - return createTupleType(elementTypes); + return addTypeFlags(createTupleType(elementTypes), typeFlags & TypeFlags.RequiresWidening); } } - return createArrayType(getUnionType(elementTypes)); + return addTypeFlags(createArrayType(getUnionType(elementTypes)), typeFlags & TypeFlags.RequiresWidening); } function isNumericName(name: DeclarationName): boolean { @@ -6934,8 +6953,7 @@ namespace ts { let stringIndexType = getIndexType(IndexKind.String); let numberIndexType = getIndexType(IndexKind.Number); let result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexType, numberIndexType); - result.flags |= TypeFlags.ObjectLiteral | TypeFlags.ContainsObjectLiteral | (typeFlags & TypeFlags.ContainsUndefinedOrNull); - return result; + return addTypeFlags(result, TypeFlags.ObjectLiteral | TypeFlags.ContainsObjectLiteral | (typeFlags & TypeFlags.ContainsUndefinedOrNull)); function getIndexType(kind: IndexKind) { if (contextualType && contextualTypeHasIndexSignature(contextualType, kind)) { @@ -6961,6 +6979,10 @@ namespace ts { } } + function addTypeFlags(type: Type, flags: TypeFlags): Type { + type.flags |= flags; + return type; + } function checkJsxSelfClosingElement(node: JsxSelfClosingElement) { checkJsxOpeningLikeElement(node); diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index a0a95d47c8c..ce28cbbc387 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -589,6 +589,7 @@ namespace ts { Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions: { code: 7024, category: DiagnosticCategory.Error, key: "Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions." }, Generator_implicitly_has_type_0_because_it_does_not_yield_any_values_Consider_supplying_a_return_type: { code: 7025, category: DiagnosticCategory.Error, key: "Generator implicitly has type '{0}' because it does not yield any values. Consider supplying a return type." }, JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists: { code: 7026, category: DiagnosticCategory.Error, key: "JSX element implicitly has type 'any' because no interface 'JSX.{0}' exists" }, + Array_element_at_index_0_implicitly_has_an_1_type: { code: 7027, category: DiagnosticCategory.Error, key: "Array element at index {0} implicitly has an '{1}' type." }, You_cannot_rename_this_element: { code: 8000, category: DiagnosticCategory.Error, key: "You cannot rename this element." }, You_cannot_rename_elements_that_are_defined_in_the_standard_TypeScript_library: { code: 8001, category: DiagnosticCategory.Error, key: "You cannot rename elements that are defined in the standard TypeScript library." }, import_can_only_be_used_in_a_ts_file: { code: 8002, category: DiagnosticCategory.Error, key: "'import ... =' can only be used in a .ts file." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index acf4e74ae9f..65e3bab9ec5 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2347,6 +2347,14 @@ "category": "Error", "code": 7026 }, + "JSX element implicitly has type 'any' because no interface 'JSX.{0}' exists": { + "category": "Error", + "code": 7026 + }, + "Array element at index {0} implicitly has an '{1}' type.": { + "category": "Error", + "code": 7027 + }, "You cannot rename this element.": { From 6954cd2fd72b872c7317cad6e04e34f81f252565 Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Tue, 14 Jul 2015 11:49:50 -0700 Subject: [PATCH 2/4] Add new tests --- tests/cases/conformance/types/tuple/wideningTuples1.ts | 5 +++++ tests/cases/conformance/types/tuple/wideningTuples2.ts | 6 ++++++ tests/cases/conformance/types/tuple/wideningTuples3.ts | 4 ++++ tests/cases/conformance/types/tuple/wideningTuples4.ts | 4 ++++ tests/cases/conformance/types/tuple/wideningTuples5.ts | 2 ++ tests/cases/conformance/types/tuple/wideningTuples6.ts | 3 +++ tests/cases/conformance/types/tuple/wideningTuples7.ts | 5 +++++ 7 files changed, 29 insertions(+) create mode 100644 tests/cases/conformance/types/tuple/wideningTuples1.ts create mode 100644 tests/cases/conformance/types/tuple/wideningTuples2.ts create mode 100644 tests/cases/conformance/types/tuple/wideningTuples3.ts create mode 100644 tests/cases/conformance/types/tuple/wideningTuples4.ts create mode 100644 tests/cases/conformance/types/tuple/wideningTuples5.ts create mode 100644 tests/cases/conformance/types/tuple/wideningTuples6.ts create mode 100644 tests/cases/conformance/types/tuple/wideningTuples7.ts 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 From 6f94314774c0a2788904caf6966296dea2e7382c Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Tue, 14 Jul 2015 11:50:00 -0700 Subject: [PATCH 3/4] Accept baselines --- ...cturingParameterDeclaration1ES5.errors.txt | 20 +---------------- tests/baselines/reference/wideningTuples1.js | 9 ++++++++ .../reference/wideningTuples1.symbols | 16 ++++++++++++++ .../baselines/reference/wideningTuples1.types | 21 ++++++++++++++++++ tests/baselines/reference/wideningTuples2.js | 13 +++++++++++ .../reference/wideningTuples2.symbols | 16 ++++++++++++++ .../baselines/reference/wideningTuples2.types | 22 +++++++++++++++++++ .../reference/wideningTuples3.errors.txt | 9 ++++++++ tests/baselines/reference/wideningTuples3.js | 8 +++++++ tests/baselines/reference/wideningTuples4.js | 10 +++++++++ .../reference/wideningTuples4.symbols | 12 ++++++++++ .../baselines/reference/wideningTuples4.types | 19 ++++++++++++++++ .../reference/wideningTuples5.errors.txt | 10 +++++++++ tests/baselines/reference/wideningTuples5.js | 5 +++++ tests/baselines/reference/wideningTuples6.js | 9 ++++++++ .../reference/wideningTuples6.symbols | 12 ++++++++++ .../baselines/reference/wideningTuples6.types | 18 +++++++++++++++ .../reference/wideningTuples7.errors.txt | 10 +++++++++ tests/baselines/reference/wideningTuples7.js | 11 ++++++++++ 19 files changed, 231 insertions(+), 19 deletions(-) create mode 100644 tests/baselines/reference/wideningTuples1.js create mode 100644 tests/baselines/reference/wideningTuples1.symbols create mode 100644 tests/baselines/reference/wideningTuples1.types create mode 100644 tests/baselines/reference/wideningTuples2.js create mode 100644 tests/baselines/reference/wideningTuples2.symbols create mode 100644 tests/baselines/reference/wideningTuples2.types create mode 100644 tests/baselines/reference/wideningTuples3.errors.txt create mode 100644 tests/baselines/reference/wideningTuples3.js create mode 100644 tests/baselines/reference/wideningTuples4.js create mode 100644 tests/baselines/reference/wideningTuples4.symbols create mode 100644 tests/baselines/reference/wideningTuples4.types create mode 100644 tests/baselines/reference/wideningTuples5.errors.txt create mode 100644 tests/baselines/reference/wideningTuples5.js create mode 100644 tests/baselines/reference/wideningTuples6.js create mode 100644 tests/baselines/reference/wideningTuples6.symbols create mode 100644 tests/baselines/reference/wideningTuples6.types create mode 100644 tests/baselines/reference/wideningTuples7.errors.txt create mode 100644 tests/baselines/reference/wideningTuples7.js 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]; +}; From 6fc08ea3e62baee65235abcb23c7572b8c61140d Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Tue, 14 Jul 2015 13:44:40 -0700 Subject: [PATCH 4/4] Clean up and address CR feedback --- src/compiler/checker.ts | 55 +++++++++---------- .../diagnosticInformationMap.generated.ts | 1 - src/compiler/diagnosticMessages.json | 8 --- 3 files changed, 25 insertions(+), 39 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5209f02847e..20e0af5e873 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; @@ -5300,7 +5300,7 @@ namespace ts { * Prefer using isTupleLikeType() unless the use of `elementTypes` is required. */ function isTupleType(type: Type): type is TupleType { - return (type.flags & TypeFlags.Tuple) && !!(type).elementTypes; + return !!(type.flags & TypeFlags.Tuple); } function getWidenedTypeOfObjectLiteral(type: Type): Type { @@ -5348,31 +5348,33 @@ namespace ts { return type; } - function reportWideningErrorsInType(type: Type, expression: Expression): boolean { + /** + * 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) { for (let t of (type).types) { - if (reportWideningErrorsInType(t, expression)) { + if (reportWideningErrorsInType(t)) { errorReported = true; } } } if (isArrayType(type)) { - return reportWideningErrorsInType((type).typeArguments[0], expression); + return reportWideningErrorsInType((type).typeArguments[0]); } if (isTupleType(type)) { - let { elementTypes } = type; - for (let i = 0; i < elementTypes.length; i++) { - let t = elementTypes[i]; - if (t.flags & TypeFlags.ContainsUndefinedOrNull) { - if (reportWideningErrorsInType(t, expression)) { - errorReported = true; - } - else if (expression.kind === SyntaxKind.ArrayLiteralExpression) { - let element = (expression).elements[i]; - error(element, Diagnostics.Array_element_at_index_0_implicitly_has_an_1_type, i, typeToString(getWidenedType(t))); - errorReported = true; - } + for (let t of type.elementTypes) { + if (reportWideningErrorsInType(t)) { + errorReported = true; } } } @@ -5380,7 +5382,7 @@ namespace ts { for (let p of getPropertiesOfObjectType(type)) { let t = getTypeOfSymbol(p); if (t.flags & TypeFlags.ContainsUndefinedOrNull) { - if (!reportWideningErrorsInType(t, expression)) { + if (!reportWideningErrorsInType(t)) { error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, p.name, typeToString(getWidenedType(t))); } errorReported = true; @@ -5425,7 +5427,7 @@ namespace ts { function reportErrorsFromWidening(declaration: Declaration, type: Type) { if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsUndefinedOrNull) { // Report implicit any error within type if possible, otherwise report error on declaration - if (!reportWideningErrorsInType(type, declaration.kind === SyntaxKind.VariableDeclaration && (declaration).initializer)) { + if (!reportWideningErrorsInType(type)) { reportImplicitAnyError(declaration, type); } } @@ -6801,7 +6803,6 @@ namespace ts { let hasSpreadElement = false; let elementTypes: Type[] = []; let inDestructuringPattern = isAssignmentTarget(node); - let typeFlags: TypeFlags; for (let e of elements) { if (inDestructuringPattern && e.kind === SyntaxKind.SpreadElementExpression) { // Given the following situation: @@ -6821,23 +6822,21 @@ namespace ts { (languageVersion >= ScriptTarget.ES6 ? getElementTypeOfIterable(restArrayType, /*errorNode*/ undefined) : undefined); if (restElementType) { elementTypes.push(restElementType); - typeFlags |= restElementType.flags; } } else { let type = checkExpression(e, contextualMapper); elementTypes.push(type); - typeFlags |= type.flags; } hasSpreadElement = hasSpreadElement || e.kind === SyntaxKind.SpreadElementExpression; } if (!hasSpreadElement) { let contextualType = getContextualType(node); if (contextualType && contextualTypeIsTupleLikeType(contextualType) || inDestructuringPattern) { - return addTypeFlags(createTupleType(elementTypes), typeFlags & TypeFlags.RequiresWidening); + return createTupleType(elementTypes); } } - return addTypeFlags(createArrayType(getUnionType(elementTypes)), typeFlags & TypeFlags.RequiresWidening); + return createArrayType(getUnionType(elementTypes)); } function isNumericName(name: DeclarationName): boolean { @@ -6953,7 +6952,8 @@ namespace ts { let stringIndexType = getIndexType(IndexKind.String); let numberIndexType = getIndexType(IndexKind.Number); let result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexType, numberIndexType); - return addTypeFlags(result, TypeFlags.ObjectLiteral | TypeFlags.ContainsObjectLiteral | (typeFlags & TypeFlags.ContainsUndefinedOrNull)); + result.flags |= TypeFlags.ObjectLiteral | TypeFlags.ContainsObjectLiteral | (typeFlags & TypeFlags.ContainsUndefinedOrNull); + return result; function getIndexType(kind: IndexKind) { if (contextualType && contextualTypeHasIndexSignature(contextualType, kind)) { @@ -6979,11 +6979,6 @@ namespace ts { } } - function addTypeFlags(type: Type, flags: TypeFlags): Type { - type.flags |= flags; - return type; - } - function checkJsxSelfClosingElement(node: JsxSelfClosingElement) { checkJsxOpeningLikeElement(node); return jsxElementType || anyType; diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index ce28cbbc387..a0a95d47c8c 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -589,7 +589,6 @@ namespace ts { Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions: { code: 7024, category: DiagnosticCategory.Error, key: "Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions." }, Generator_implicitly_has_type_0_because_it_does_not_yield_any_values_Consider_supplying_a_return_type: { code: 7025, category: DiagnosticCategory.Error, key: "Generator implicitly has type '{0}' because it does not yield any values. Consider supplying a return type." }, JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists: { code: 7026, category: DiagnosticCategory.Error, key: "JSX element implicitly has type 'any' because no interface 'JSX.{0}' exists" }, - Array_element_at_index_0_implicitly_has_an_1_type: { code: 7027, category: DiagnosticCategory.Error, key: "Array element at index {0} implicitly has an '{1}' type." }, You_cannot_rename_this_element: { code: 8000, category: DiagnosticCategory.Error, key: "You cannot rename this element." }, You_cannot_rename_elements_that_are_defined_in_the_standard_TypeScript_library: { code: 8001, category: DiagnosticCategory.Error, key: "You cannot rename elements that are defined in the standard TypeScript library." }, import_can_only_be_used_in_a_ts_file: { code: 8002, category: DiagnosticCategory.Error, key: "'import ... =' can only be used in a .ts file." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 65e3bab9ec5..acf4e74ae9f 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2347,14 +2347,6 @@ "category": "Error", "code": 7026 }, - "JSX element implicitly has type 'any' because no interface 'JSX.{0}' exists": { - "category": "Error", - "code": 7026 - }, - "Array element at index {0} implicitly has an '{1}' type.": { - "category": "Error", - "code": 7027 - }, "You cannot rename this element.": {