From 429e1b4d712e5b8953de52725d8a698055f13911 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 26 Sep 2017 15:12:46 -0700 Subject: [PATCH 1/4] Make it a noImplicitAny error to fail to provide type arguments to a superclass via @augments --- src/compiler/checker.ts | 31 ++++++++++--------- src/compiler/diagnosticMessages.json | 8 +++++ .../reference/jsExtendsImplicitAny.errors.txt | 11 +++++++ .../reference/jsExtendsImplicitAny.symbols | 12 +++++++ .../reference/jsExtendsImplicitAny.types | 12 +++++++ tests/cases/compiler/jsExtendsImplicitAny.ts | 10 ++++++ 6 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 tests/baselines/reference/jsExtendsImplicitAny.errors.txt create mode 100644 tests/baselines/reference/jsExtendsImplicitAny.symbols create mode 100644 tests/baselines/reference/jsExtendsImplicitAny.types create mode 100644 tests/cases/compiler/jsExtendsImplicitAny.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d89247b70f978..2b966507d3a4e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6352,11 +6352,11 @@ namespace ts { * @param typeParameters The requested type parameters. * @param minTypeArgumentCount The minimum number of required type arguments. */ - function fillMissingTypeArguments(typeArguments: Type[] | undefined, typeParameters: TypeParameter[] | undefined, minTypeArgumentCount: number, isJavaScript: boolean) { + function fillMissingTypeArguments(typeArguments: Type[] | undefined, typeParameters: TypeParameter[] | undefined, minTypeArgumentCount: number, isJavaScriptImplicitAny: boolean) { const numTypeParameters = length(typeParameters); if (numTypeParameters) { const numTypeArguments = length(typeArguments); - if ((isJavaScript || numTypeArguments >= minTypeArgumentCount) && numTypeArguments <= numTypeParameters) { + if ((isJavaScriptImplicitAny || numTypeArguments >= minTypeArgumentCount) && numTypeArguments <= numTypeParameters) { if (!typeArguments) { typeArguments = []; } @@ -6365,12 +6365,12 @@ namespace ts { // If a type parameter does not have a default type, or if the default type // is a forward reference, the empty object type is used. for (let i = numTypeArguments; i < numTypeParameters; i++) { - typeArguments[i] = getDefaultTypeArgumentType(isJavaScript); + typeArguments[i] = getDefaultTypeArgumentType(isJavaScriptImplicitAny); } for (let i = numTypeArguments; i < numTypeParameters; i++) { const mapper = createTypeMapper(typeParameters, typeArguments); const defaultType = getDefaultFromTypeParameter(typeParameters[i]); - typeArguments[i] = defaultType ? instantiateType(defaultType, mapper) : getDefaultTypeArgumentType(isJavaScript); + typeArguments[i] = defaultType ? instantiateType(defaultType, mapper) : getDefaultTypeArgumentType(isJavaScriptImplicitAny); } } } @@ -6806,21 +6806,24 @@ namespace ts { if (typeParameters) { const numTypeArguments = length(node.typeArguments); const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters); - const isJavascript = isInJavaScriptFile(node); - if (!isJavascript && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) { - error(node, - minTypeArgumentCount === typeParameters.length - ? Diagnostics.Generic_type_0_requires_1_type_argument_s - : Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments, - typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType), - minTypeArgumentCount, - typeParameters.length); + const isJs = isInJavaScriptFile(node); + const isJsImplicitAny = !compilerOptions.noImplicitAny && isJs; + if (!isJsImplicitAny && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) { + const diag = minTypeArgumentCount === typeParameters.length + ? isJs + ? Diagnostics.Generic_type_0_requires_1_type_arguments_provide_these_with_an_augments_or_extends_tag + : Diagnostics.Generic_type_0_requires_1_type_argument_s + : isJs + ? Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments_provide_these_with_an_augments_or_extends_tag + : Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments; + const typeStr = typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType); + error(node, diag, typeStr, minTypeArgumentCount, typeParameters.length); return unknownType; } // In a type reference, the outer type parameters of the referenced class or interface are automatically // supplied as type arguments and the type reference only specifies arguments for the local type parameters // of the class or interface. - const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgs, typeParameters, minTypeArgumentCount, isJavascript)); + const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgs, typeParameters, minTypeArgumentCount, isJsImplicitAny)); return createTypeReference(type, typeArguments); } if (node.typeArguments) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 2a8b74d64bdf2..f42190e2ac2d7 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3511,6 +3511,14 @@ "category": "Error", "code": 8021 }, + "Generic type '{0}' requires '{1}' type arguments; provide these with an '@augments' or '@extends' tag.": { + "category": "Error", + "code": 8022 + }, + "Generic type '{0}' requires between '{1}' and '{2}' type arguments; provide these with an '@augments' or '@extends' tag.": { + "category": "Error", + "code": 8023 + }, "Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": { "category": "Error", "code": 9002 diff --git a/tests/baselines/reference/jsExtendsImplicitAny.errors.txt b/tests/baselines/reference/jsExtendsImplicitAny.errors.txt new file mode 100644 index 0000000000000..186233316ee86 --- /dev/null +++ b/tests/baselines/reference/jsExtendsImplicitAny.errors.txt @@ -0,0 +1,11 @@ +/b.js(1,17): error TS8022: Generic type 'A' requires '1' type arguments; provide these with an '@augments' or '@extends' tag. + + +==== /a.d.ts (0 errors) ==== + declare class A { x: T; } + +==== /b.js (1 errors) ==== + class B extends A {} + ~ +!!! error TS8022: Generic type 'A' requires '1' type arguments; provide these with an '@augments' or '@extends' tag. + \ No newline at end of file diff --git a/tests/baselines/reference/jsExtendsImplicitAny.symbols b/tests/baselines/reference/jsExtendsImplicitAny.symbols new file mode 100644 index 0000000000000..fa8267757be97 --- /dev/null +++ b/tests/baselines/reference/jsExtendsImplicitAny.symbols @@ -0,0 +1,12 @@ +=== /a.d.ts === +declare class A { x: T; } +>A : Symbol(A, Decl(a.d.ts, 0, 0)) +>T : Symbol(T, Decl(a.d.ts, 0, 16)) +>x : Symbol(A.x, Decl(a.d.ts, 0, 20)) +>T : Symbol(T, Decl(a.d.ts, 0, 16)) + +=== /b.js === +class B extends A {} +>B : Symbol(B, Decl(b.js, 0, 0)) +>A : Symbol(A, Decl(a.d.ts, 0, 0)) + diff --git a/tests/baselines/reference/jsExtendsImplicitAny.types b/tests/baselines/reference/jsExtendsImplicitAny.types new file mode 100644 index 0000000000000..d462e2ac9d556 --- /dev/null +++ b/tests/baselines/reference/jsExtendsImplicitAny.types @@ -0,0 +1,12 @@ +=== /a.d.ts === +declare class A { x: T; } +>A : A +>T : T +>x : T +>T : T + +=== /b.js === +class B extends A {} +>B : B +>A : typeof A + diff --git a/tests/cases/compiler/jsExtendsImplicitAny.ts b/tests/cases/compiler/jsExtendsImplicitAny.ts new file mode 100644 index 0000000000000..3191e3333a3c2 --- /dev/null +++ b/tests/cases/compiler/jsExtendsImplicitAny.ts @@ -0,0 +1,10 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @noImplicitAny: true + +// @Filename: /a.d.ts +declare class A { x: T; } + +// @Filename: /b.js +class B extends A {} From 6e1488db866c2bc61a7b8cf3b1bb4fa3d87d0974 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 27 Sep 2017 14:05:46 -0700 Subject: [PATCH 2/4] Don't recommend to add an @augments tag if it already exists --- src/compiler/checker.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2b966507d3a4e..e701e8775e819 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6809,11 +6809,12 @@ namespace ts { const isJs = isInJavaScriptFile(node); const isJsImplicitAny = !compilerOptions.noImplicitAny && isJs; if (!isJsImplicitAny && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) { + const missingAugmentsTag = isJs && node.parent.kind !== SyntaxKind.JSDocAugmentsTag; const diag = minTypeArgumentCount === typeParameters.length - ? isJs + ? missingAugmentsTag ? Diagnostics.Generic_type_0_requires_1_type_arguments_provide_these_with_an_augments_or_extends_tag : Diagnostics.Generic_type_0_requires_1_type_argument_s - : isJs + : missingAugmentsTag ? Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments_provide_these_with_an_augments_or_extends_tag : Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments; const typeStr = typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType); From 6f584cb572821dcddfcc2a2d59c16e0141734d21 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 29 Sep 2017 09:54:48 -0700 Subject: [PATCH 3/4] Suggestions from code review --- src/compiler/checker.ts | 4 ++-- src/compiler/diagnosticMessages.json | 4 ++-- .../reference/jsExtendsImplicitAny.errors.txt | 12 +++++++++--- .../baselines/reference/jsExtendsImplicitAny.symbols | 4 ++++ tests/baselines/reference/jsExtendsImplicitAny.types | 4 ++++ tests/cases/compiler/jsExtendsImplicitAny.ts | 3 +++ 6 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cb15b8269a592..0259b2d9c31d9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6816,10 +6816,10 @@ namespace ts { const missingAugmentsTag = isJs && node.parent.kind !== SyntaxKind.JSDocAugmentsTag; const diag = minTypeArgumentCount === typeParameters.length ? missingAugmentsTag - ? Diagnostics.Generic_type_0_requires_1_type_arguments_provide_these_with_an_augments_or_extends_tag + ? Diagnostics.Generic_type_0_requires_1_type_arguments_provide_these_with_an_extends_tag : Diagnostics.Generic_type_0_requires_1_type_argument_s : missingAugmentsTag - ? Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments_provide_these_with_an_augments_or_extends_tag + ? Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments_provide_these_with_an_extends_tag : Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments; const typeStr = typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType); error(node, diag, typeStr, minTypeArgumentCount, typeParameters.length); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 17b875c8dccb0..c62eb48007017 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3523,11 +3523,11 @@ "category": "Error", "code": 8024 }, - "Generic type '{0}' requires '{1}' type arguments; provide these with an '@augments' or '@extends' tag.": { + "Generic type '{0}' requires '{1}' type arguments; provide these with an '@extends' tag.": { "category": "Error", "code": 8025 }, - "Generic type '{0}' requires between '{1}' and '{2}' type arguments; provide these with an '@augments' or '@extends' tag.": { + "Generic type '{0}' requires between '{1}' and '{2}' type arguments; provide these with an '@extends' tag.": { "category": "Error", "code": 8026 }, diff --git a/tests/baselines/reference/jsExtendsImplicitAny.errors.txt b/tests/baselines/reference/jsExtendsImplicitAny.errors.txt index 186233316ee86..849e937e67cb8 100644 --- a/tests/baselines/reference/jsExtendsImplicitAny.errors.txt +++ b/tests/baselines/reference/jsExtendsImplicitAny.errors.txt @@ -1,11 +1,17 @@ -/b.js(1,17): error TS8022: Generic type 'A' requires '1' type arguments; provide these with an '@augments' or '@extends' tag. +/b.js(1,17): error TS8025: Generic type 'A' requires '1' type arguments; provide these with an '@extends' tag. +/b.js(3,15): error TS2314: Generic type 'A' requires 1 type argument(s). ==== /a.d.ts (0 errors) ==== declare class A { x: T; } -==== /b.js (1 errors) ==== +==== /b.js (2 errors) ==== class B extends A {} ~ -!!! error TS8022: Generic type 'A' requires '1' type arguments; provide these with an '@augments' or '@extends' tag. +!!! error TS8025: Generic type 'A' requires '1' type arguments; provide these with an '@extends' tag. + + /** @augments A */ + ~ +!!! error TS2314: Generic type 'A' requires 1 type argument(s). + class C { } \ No newline at end of file diff --git a/tests/baselines/reference/jsExtendsImplicitAny.symbols b/tests/baselines/reference/jsExtendsImplicitAny.symbols index fa8267757be97..0a6d71e109ffe 100644 --- a/tests/baselines/reference/jsExtendsImplicitAny.symbols +++ b/tests/baselines/reference/jsExtendsImplicitAny.symbols @@ -10,3 +10,7 @@ class B extends A {} >B : Symbol(B, Decl(b.js, 0, 0)) >A : Symbol(A, Decl(a.d.ts, 0, 0)) +/** @augments A */ +class C { } +>C : Symbol(C, Decl(b.js, 0, 20)) + diff --git a/tests/baselines/reference/jsExtendsImplicitAny.types b/tests/baselines/reference/jsExtendsImplicitAny.types index d462e2ac9d556..ba98ef3ed2362 100644 --- a/tests/baselines/reference/jsExtendsImplicitAny.types +++ b/tests/baselines/reference/jsExtendsImplicitAny.types @@ -10,3 +10,7 @@ class B extends A {} >B : B >A : typeof A +/** @augments A */ +class C { } +>C : C + diff --git a/tests/cases/compiler/jsExtendsImplicitAny.ts b/tests/cases/compiler/jsExtendsImplicitAny.ts index 3191e3333a3c2..e3d737ceb3590 100644 --- a/tests/cases/compiler/jsExtendsImplicitAny.ts +++ b/tests/cases/compiler/jsExtendsImplicitAny.ts @@ -8,3 +8,6 @@ declare class A { x: T; } // @Filename: /b.js class B extends A {} + +/** @augments A */ +class C { } From c2043b102f286ccf318423fd1e19ead0813a6349 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 20 Oct 2017 09:10:25 -0700 Subject: [PATCH 4/4] Shorten error message --- src/compiler/checker.ts | 4 ++-- src/compiler/diagnosticMessages.json | 4 ++-- tests/baselines/reference/jsExtendsImplicitAny.errors.txt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cf965e9322c0b..da3dac4d60504 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6880,10 +6880,10 @@ namespace ts { const missingAugmentsTag = isJs && node.parent.kind !== SyntaxKind.JSDocAugmentsTag; const diag = minTypeArgumentCount === typeParameters.length ? missingAugmentsTag - ? Diagnostics.Generic_type_0_requires_1_type_arguments_provide_these_with_an_extends_tag + ? Diagnostics.Expected_0_type_arguments_provide_these_with_an_extends_tag : Diagnostics.Generic_type_0_requires_1_type_argument_s : missingAugmentsTag - ? Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments_provide_these_with_an_extends_tag + ? Diagnostics.Expected_0_1_type_arguments_provide_these_with_an_extends_tag : Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments; const typeStr = typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType); error(node, diag, typeStr, minTypeArgumentCount, typeParameters.length); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 94e9eb36022c6..0f88d5e4ac567 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3539,11 +3539,11 @@ "category": "Error", "code": 8025 }, - "Generic type '{0}' requires '{1}' type arguments; provide these with an '@extends' tag.": { + "Expected {0} type arguments; provide these with an '@extends' tag.": { "category": "Error", "code": 8026 }, - "Generic type '{0}' requires between '{1}' and '{2}' type arguments; provide these with an '@extends' tag.": { + "Expected {0}-{1} type arguments; provide these with an '@extends' tag.": { "category": "Error", "code": 8027 }, diff --git a/tests/baselines/reference/jsExtendsImplicitAny.errors.txt b/tests/baselines/reference/jsExtendsImplicitAny.errors.txt index 849e937e67cb8..4b836970e00a9 100644 --- a/tests/baselines/reference/jsExtendsImplicitAny.errors.txt +++ b/tests/baselines/reference/jsExtendsImplicitAny.errors.txt @@ -1,4 +1,4 @@ -/b.js(1,17): error TS8025: Generic type 'A' requires '1' type arguments; provide these with an '@extends' tag. +/b.js(1,17): error TS8026: Expected A type arguments; provide these with an '@extends' tag. /b.js(3,15): error TS2314: Generic type 'A' requires 1 type argument(s). @@ -8,7 +8,7 @@ ==== /b.js (2 errors) ==== class B extends A {} ~ -!!! error TS8025: Generic type 'A' requires '1' type arguments; provide these with an '@extends' tag. +!!! error TS8026: Expected A type arguments; provide these with an '@extends' tag. /** @augments A */ ~