mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
Add missing type argument constraints check (#51766)
* Add missing type argument constraints check * Leverage existing routine for obtaining typeParameters
This commit is contained in:
parent
eb9252ed9e
commit
aa2781d44f
@ -17606,6 +17606,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return getTypeOfSymbol(symbol); // intentionally doesn't use resolved symbol so type is cached as expected on the alias
|
||||
}
|
||||
else {
|
||||
const type = tryGetDeclaredTypeOfSymbol(resolvedSymbol); // call this first to ensure typeParameters is populated (if applicable)
|
||||
const typeParameters = type && getTypeParametersForTypeAndSymbol(type, resolvedSymbol);
|
||||
if (node.typeArguments && typeParameters) {
|
||||
addLazyDiagnostic(() => {
|
||||
checkTypeArgumentConstraints(node, typeParameters);
|
||||
});
|
||||
}
|
||||
return getTypeReferenceType(node, resolvedSymbol); // getTypeReferenceType doesn't handle aliases - it must get the resolved symbol
|
||||
}
|
||||
}
|
||||
@ -37264,12 +37271,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return getEffectiveTypeArguments(node, typeParameters)[index];
|
||||
}
|
||||
|
||||
function getEffectiveTypeArguments(node: TypeReferenceNode | ExpressionWithTypeArguments, typeParameters: readonly TypeParameter[]): Type[] {
|
||||
function getEffectiveTypeArguments(node: TypeReferenceNode | ExpressionWithTypeArguments | NodeWithTypeArguments, typeParameters: readonly TypeParameter[]): Type[] {
|
||||
return fillMissingTypeArguments(map(node.typeArguments!, getTypeFromTypeNode), typeParameters,
|
||||
getMinTypeArgumentCount(typeParameters), isInJSFile(node));
|
||||
}
|
||||
|
||||
function checkTypeArgumentConstraints(node: TypeReferenceNode | ExpressionWithTypeArguments, typeParameters: readonly TypeParameter[]): boolean {
|
||||
function checkTypeArgumentConstraints(node: TypeReferenceNode | ExpressionWithTypeArguments | NodeWithTypeArguments, typeParameters: readonly TypeParameter[]): boolean {
|
||||
let typeArguments: Type[] | undefined;
|
||||
let mapper: TypeMapper | undefined;
|
||||
let result = true;
|
||||
@ -37290,13 +37297,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return result;
|
||||
}
|
||||
|
||||
function getTypeParametersForTypeAndSymbol(type: Type, symbol: Symbol) {
|
||||
if (!isErrorType(type)) {
|
||||
return symbol.flags & SymbolFlags.TypeAlias && getSymbolLinks(symbol).typeParameters ||
|
||||
(getObjectFlags(type) & ObjectFlags.Reference ? (type as TypeReference).target.localTypeParameters : undefined);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getTypeParametersForTypeReference(node: TypeReferenceNode | ExpressionWithTypeArguments) {
|
||||
const type = getTypeFromTypeReference(node);
|
||||
if (!isErrorType(type)) {
|
||||
const symbol = getNodeLinks(node).resolvedSymbol;
|
||||
if (symbol) {
|
||||
return symbol.flags & SymbolFlags.TypeAlias && getSymbolLinks(symbol).typeParameters ||
|
||||
(getObjectFlags(type) & ObjectFlags.Reference ? (type as TypeReference).target.localTypeParameters : undefined);
|
||||
return getTypeParametersForTypeAndSymbol(type, symbol);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
tests/cases/compiler/file2.ts(1,37): error TS2344: Type 'T' does not satisfy the constraint 'string'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/file1.ts (0 errors) ====
|
||||
export type Foo<T extends string> = { foo: T }
|
||||
|
||||
==== tests/cases/compiler/file2.ts (1 errors) ====
|
||||
type Bar<T> = import('./file1').Foo<T>;
|
||||
~
|
||||
!!! error TS2344: Type 'T' does not satisfy the constraint 'string'.
|
||||
!!! related TS2208 tests/cases/compiler/file2.ts:1:10: This type parameter might need an `extends string` constraint.
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
=== tests/cases/compiler/file1.ts ===
|
||||
export type Foo<T extends string> = { foo: T }
|
||||
>Foo : Symbol(Foo, Decl(file1.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(file1.ts, 0, 16))
|
||||
>foo : Symbol(foo, Decl(file1.ts, 0, 37))
|
||||
>T : Symbol(T, Decl(file1.ts, 0, 16))
|
||||
|
||||
=== tests/cases/compiler/file2.ts ===
|
||||
type Bar<T> = import('./file1').Foo<T>;
|
||||
>Bar : Symbol(Bar, Decl(file2.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(file2.ts, 0, 9))
|
||||
>Foo : Symbol(Foo, Decl(file1.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(file2.ts, 0, 9))
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
=== tests/cases/compiler/file1.ts ===
|
||||
export type Foo<T extends string> = { foo: T }
|
||||
>Foo : Foo<T>
|
||||
>foo : T
|
||||
|
||||
=== tests/cases/compiler/file2.ts ===
|
||||
type Bar<T> = import('./file1').Foo<T>;
|
||||
>Bar : Bar<T>
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
tests/cases/compiler/file2.js(3,36): error TS2344: Type 'T' does not satisfy the constraint 'string'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/file1.js (0 errors) ====
|
||||
/**
|
||||
* @template {string} T
|
||||
* @typedef {{ foo: T }} Foo
|
||||
*/
|
||||
|
||||
export default {};
|
||||
|
||||
==== tests/cases/compiler/file2.js (1 errors) ====
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {import('./file1').Foo<T>} Bar
|
||||
~
|
||||
!!! error TS2344: Type 'T' does not satisfy the constraint 'string'.
|
||||
!!! related TS2208 tests/cases/compiler/file2.js:2:14: This type parameter might need an `extends string` constraint.
|
||||
*/
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
=== tests/cases/compiler/file1.js ===
|
||||
|
||||
/**
|
||||
* @template {string} T
|
||||
* @typedef {{ foo: T }} Foo
|
||||
*/
|
||||
|
||||
export default {};
|
||||
|
||||
=== tests/cases/compiler/file2.js ===
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {import('./file1').Foo<T>} Bar
|
||||
*/
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
=== tests/cases/compiler/file1.js ===
|
||||
/**
|
||||
* @template {string} T
|
||||
* @typedef {{ foo: T }} Foo
|
||||
*/
|
||||
|
||||
export default {};
|
||||
>{} : {}
|
||||
|
||||
=== tests/cases/compiler/file2.js ===
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {import('./file1').Foo<T>} Bar
|
||||
*/
|
||||
|
||||
7
tests/cases/compiler/unmetTypeConstraintInImportCall.ts
Normal file
7
tests/cases/compiler/unmetTypeConstraintInImportCall.ts
Normal file
@ -0,0 +1,7 @@
|
||||
// @noEmit: true
|
||||
// @filename: file1.ts
|
||||
export type Foo<T extends string> = { foo: T }
|
||||
|
||||
// @noEmit: true
|
||||
// @filename: file2.ts
|
||||
type Bar<T> = import('./file1').Foo<T>;
|
||||
19
tests/cases/compiler/unmetTypeConstraintInJSDocImportCall.ts
Normal file
19
tests/cases/compiler/unmetTypeConstraintInJSDocImportCall.ts
Normal file
@ -0,0 +1,19 @@
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noEmit: true
|
||||
// @filename: file1.js
|
||||
/**
|
||||
* @template {string} T
|
||||
* @typedef {{ foo: T }} Foo
|
||||
*/
|
||||
|
||||
export default {};
|
||||
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noEmit: true
|
||||
// @filename: file2.js
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {import('./file1').Foo<T>} Bar
|
||||
*/
|
||||
Loading…
x
Reference in New Issue
Block a user