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:
parent
d7be61a569
commit
8b7d859fb3
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
17
tests/baselines/reference/jsExtendsImplicitAny.errors.txt
Normal file
17
tests/baselines/reference/jsExtendsImplicitAny.errors.txt
Normal 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 { }
|
||||
|
16
tests/baselines/reference/jsExtendsImplicitAny.symbols
Normal file
16
tests/baselines/reference/jsExtendsImplicitAny.symbols
Normal 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))
|
||||
|
16
tests/baselines/reference/jsExtendsImplicitAny.types
Normal file
16
tests/baselines/reference/jsExtendsImplicitAny.types
Normal 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
|
||||
|
13
tests/cases/compiler/jsExtendsImplicitAny.ts
Normal file
13
tests/cases/compiler/jsExtendsImplicitAny.ts
Normal 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 { }
|
Loading…
Reference in a new issue