Make it a noImplicitAny error to fail to provide type arguments to a superclass via @augments (#18778)

* Make it a noImplicitAny error to fail to provide type arguments to a superclass via @augments

* Don't recommend to add an @augments tag if it already exists

* Suggestions from code review

* Shorten error message
This commit is contained in:
Andy 2017-10-20 09:41:19 -07:00 committed by GitHub
parent d7be61a569
commit 8b7d859fb3
6 changed files with 88 additions and 14 deletions

View file

@ -6420,11 +6420,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 = [];
}
@ -6433,12 +6433,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);
}
}
}
@ -6874,21 +6874,25 @@ 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 missingAugmentsTag = isJs && node.parent.kind !== SyntaxKind.JSDocAugmentsTag;
const diag = minTypeArgumentCount === typeParameters.length
? missingAugmentsTag
? Diagnostics.Expected_0_type_arguments_provide_these_with_an_extends_tag
: Diagnostics.Generic_type_0_requires_1_type_argument_s
: missingAugmentsTag
? 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);
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(<GenericType>type, typeArguments);
}
if (node.typeArguments) {

View file

@ -3539,6 +3539,14 @@
"category": "Error",
"code": 8025
},
"Expected {0} type arguments; provide these with an '@extends' tag.": {
"category": "Error",
"code": 8026
},
"Expected {0}-{1} type arguments; provide these with an '@extends' tag.": {
"category": "Error",
"code": 8027
},
"Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": {
"category": "Error",
"code": 9002

View file

@ -0,0 +1,17 @@
/b.js(1,17): error TS8026: Expected A<T> type arguments; provide these with an '@extends' tag.
/b.js(3,15): error TS2314: Generic type 'A<T>' requires 1 type argument(s).
==== /a.d.ts (0 errors) ====
declare class A<T> { x: T; }
==== /b.js (2 errors) ====
class B extends A {}
~
!!! error TS8026: Expected A<T> type arguments; provide these with an '@extends' tag.
/** @augments A */
~
!!! error TS2314: Generic type 'A<T>' requires 1 type argument(s).
class C { }

View file

@ -0,0 +1,16 @@
=== /a.d.ts ===
declare class A<T> { 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))
/** @augments A */
class C { }
>C : Symbol(C, Decl(b.js, 0, 20))

View file

@ -0,0 +1,16 @@
=== /a.d.ts ===
declare class A<T> { x: T; }
>A : A<T>
>T : T
>x : T
>T : T
=== /b.js ===
class B extends A {}
>B : B
>A : typeof A
/** @augments A */
class C { }
>C : C

View file

@ -0,0 +1,13 @@
// @allowJs: true
// @checkJs: true
// @noEmit: true
// @noImplicitAny: true
// @Filename: /a.d.ts
declare class A<T> { x: T; }
// @Filename: /b.js
class B extends A {}
/** @augments A */
class C { }