Skip to content

Commit a73161e

Browse files
author
Andy
authored
Don't store @template constraint in a TypeParameterDeclaration node (#26283)
* Don't store @template constraint in a TypeParameterDeclaration node * Code review * Update API
1 parent 5efd1cb commit a73161e

9 files changed

+74
-13
lines changed

src/compiler/checker.ts

+20-7
Original file line numberDiff line numberDiff line change
@@ -5950,7 +5950,8 @@ namespace ts {
59505950

59515951
/** A type parameter is thisless if its contraint is thisless, or if it has no constraint. */
59525952
function isThislessTypeParameter(node: TypeParameterDeclaration) {
5953-
return !node.constraint || isThislessType(node.constraint);
5953+
const constraint = getEffectiveConstraintOfTypeParameter(node);
5954+
return !constraint || isThislessType(constraint);
59545955
}
59555956

59565957
/**
@@ -6737,7 +6738,7 @@ namespace ts {
67376738
}
67386739

67396740
function getConstraintDeclarationForMappedType(type: MappedType) {
6740-
return type.declaration.typeParameter.constraint;
6741+
return getEffectiveConstraintOfTypeParameter(type.declaration.typeParameter);
67416742
}
67426743

67436744
function isMappedTypeWithKeyofConstraintDeclaration(type: MappedType) {
@@ -7874,7 +7875,7 @@ namespace ts {
78747875

78757876
function getConstraintDeclaration(type: TypeParameter) {
78767877
const decl = type.symbol && getDeclarationOfKind<TypeParameterDeclaration>(type.symbol, SyntaxKind.TypeParameter);
7877-
return decl && decl.constraint;
7878+
return decl && getEffectiveConstraintOfTypeParameter(decl);
78787879
}
78797880

78807881
function getInferredTypeParameterConstraint(typeParameter: TypeParameter) {
@@ -7938,7 +7939,9 @@ namespace ts {
79387939
}
79397940

79407941
function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol | undefined {
7941-
return getSymbolOfNode(getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter)!.parent);
7942+
const tp = getDeclarationOfKind<TypeParameterDeclaration>(typeParameter.symbol, SyntaxKind.TypeParameter)!;
7943+
const host = isJSDocTemplateTag(tp.parent) ? getHostSignatureFromJSDoc(tp.parent) : tp.parent;
7944+
return host && getSymbolOfNode(host);
79427945
}
79437946

79447947
function getTypeListId(types: ReadonlyArray<Type> | undefined) {
@@ -22008,7 +22011,7 @@ namespace ts {
2200822011
checkSourceElement(node.default);
2200922012
const typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node));
2201022013
if (!hasNonCircularBaseConstraint(typeParameter)) {
22011-
error(node.constraint, Diagnostics.Type_parameter_0_has_a_circular_constraint, typeToString(typeParameter));
22014+
error(getEffectiveConstraintOfTypeParameter(node), Diagnostics.Type_parameter_0_has_a_circular_constraint, typeToString(typeParameter));
2201222015
}
2201322016
if (!hasNonCircularTypeParameterDefault(typeParameter)) {
2201422017
error(node.default, Diagnostics.Type_parameter_0_has_a_circular_default, typeToString(typeParameter));
@@ -22741,7 +22744,7 @@ namespace ts {
2274122744

2274222745
const type = <MappedType>getTypeFromMappedTypeNode(node);
2274322746
const constraintType = getConstraintTypeFromMappedType(type);
22744-
checkTypeAssignableTo(constraintType, keyofConstraintType, node.typeParameter.constraint);
22747+
checkTypeAssignableTo(constraintType, keyofConstraintType, getEffectiveConstraintOfTypeParameter(node.typeParameter));
2274522748
}
2274622749

2274722750
function checkThisType(node: ThisTypeNode) {
@@ -23632,6 +23635,13 @@ namespace ts {
2363223635
checkSourceElement(node.typeExpression);
2363323636
}
2363423637

23638+
function checkJSDocTemplateTag(node: JSDocTemplateTag): void {
23639+
checkSourceElement(node.constraint);
23640+
for (const tp of node.typeParameters) {
23641+
checkSourceElement(tp);
23642+
}
23643+
}
23644+
2363523645
function checkJSDocTypeTag(node: JSDocTypeTag) {
2363623646
checkSourceElement(node.typeExpression);
2363723647
}
@@ -25422,7 +25432,8 @@ namespace ts {
2542225432

2542325433
// If the type parameter node does not have an identical constraint as the resolved
2542425434
// type parameter at this position, we report an error.
25425-
const sourceConstraint = source.constraint && getTypeFromTypeNode(source.constraint);
25435+
const constraint = getEffectiveConstraintOfTypeParameter(source);
25436+
const sourceConstraint = constraint && getTypeFromTypeNode(constraint);
2542625437
const targetConstraint = getConstraintOfTypeParameter(target);
2542725438
if (sourceConstraint) {
2542825439
// relax check if later interface augmentation has no constraint
@@ -26642,6 +26653,8 @@ namespace ts {
2664226653
case SyntaxKind.JSDocTypedefTag:
2664326654
case SyntaxKind.JSDocCallbackTag:
2664426655
return checkJSDocTypeAliasTag(node as JSDocTypedefTag);
26656+
case SyntaxKind.JSDocTemplateTag:
26657+
return checkJSDocTemplateTag(node as JSDocTemplateTag);
2664526658
case SyntaxKind.JSDocTypeTag:
2664626659
return checkJSDocTypeTag(node as JSDocTypeTag);
2664726660
case SyntaxKind.JSDocParameterTag:

src/compiler/parser.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ namespace ts {
475475
case SyntaxKind.JSDocAugmentsTag:
476476
return visitNode(cbNode, (<JSDocAugmentsTag>node).class);
477477
case SyntaxKind.JSDocTemplateTag:
478-
return visitNodes(cbNode, cbNodes, (<JSDocTemplateTag>node).typeParameters);
478+
return visitNode(cbNode, (<JSDocTemplateTag>node).constraint) || visitNodes(cbNode, cbNodes, (<JSDocTemplateTag>node).typeParameters);
479479
case SyntaxKind.JSDocTypedefTag:
480480
if ((node as JSDocTypedefTag).typeExpression &&
481481
(node as JSDocTypedefTag).typeExpression!.kind === SyntaxKind.JSDocTypeExpression) {
@@ -7049,13 +7049,10 @@ namespace ts {
70497049
typeParameters.push(typeParameter);
70507050
} while (parseOptionalJsdoc(SyntaxKind.CommaToken));
70517051

7052-
if (constraint) {
7053-
first(typeParameters).constraint = constraint.type;
7054-
}
7055-
70567052
const result = <JSDocTemplateTag>createNode(SyntaxKind.JSDocTemplateTag, atToken.pos);
70577053
result.atToken = atToken;
70587054
result.tagName = tagName;
7055+
result.constraint = constraint;
70597056
result.typeParameters = createNodeArray(typeParameters, typeParametersPos);
70607057
finishNode(result);
70617058
return result;

src/compiler/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,7 @@ namespace ts {
759759
kind: SyntaxKind.TypeParameter;
760760
parent: DeclarationWithTypeParameterChildren | InferTypeNode;
761761
name: Identifier;
762+
/** Note: Consider calling `getEffectiveConstraintOfTypeParameter` */
762763
constraint?: TypeNode;
763764
default?: TypeNode;
764765

@@ -2363,6 +2364,7 @@ namespace ts {
23632364

23642365
export interface JSDocTemplateTag extends JSDocTag {
23652366
kind: SyntaxKind.JSDocTemplateTag;
2367+
constraint: TypeNode | undefined;
23662368
typeParameters: NodeArray<TypeParameterDeclaration>;
23672369
}
23682370

src/compiler/utilities.ts

+9
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,8 @@ namespace ts {
10201020
return !isExpressionWithTypeArgumentsInClassExtendsClause(parent);
10211021
case SyntaxKind.TypeParameter:
10221022
return node === (<TypeParameterDeclaration>parent).constraint;
1023+
case SyntaxKind.JSDocTemplateTag:
1024+
return node === (<JSDocTemplateTag>parent).constraint;
10231025
case SyntaxKind.PropertyDeclaration:
10241026
case SyntaxKind.PropertySignature:
10251027
case SyntaxKind.Parameter:
@@ -5108,6 +5110,13 @@ namespace ts {
51085110
}
51095111
return node.typeParameters || (isInJavaScriptFile(node) ? getJSDocTypeParameterDeclarations(node) : emptyArray);
51105112
}
5113+
5114+
export function getEffectiveConstraintOfTypeParameter(node: TypeParameterDeclaration): TypeNode | undefined {
5115+
return node.constraint ? node.constraint
5116+
: isJSDocTemplateTag(node.parent) && node === node.parent.typeParameters[0]
5117+
? node.parent.constraint
5118+
: undefined;
5119+
}
51115120
}
51125121

51135122
// Simple node tests of the form `node.kind === SyntaxKind.Foo`.

tests/baselines/reference/api/tsserverlibrary.d.ts

+3
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ declare namespace ts {
548548
kind: SyntaxKind.TypeParameter;
549549
parent: DeclarationWithTypeParameterChildren | InferTypeNode;
550550
name: Identifier;
551+
/** Note: Consider calling `getEffectiveConstraintOfTypeParameter` */
551552
constraint?: TypeNode;
552553
default?: TypeNode;
553554
expression?: Expression;
@@ -1568,6 +1569,7 @@ declare namespace ts {
15681569
}
15691570
interface JSDocTemplateTag extends JSDocTag {
15701571
kind: SyntaxKind.JSDocTemplateTag;
1572+
constraint: TypeNode | undefined;
15711573
typeParameters: NodeArray<TypeParameterDeclaration>;
15721574
}
15731575
interface JSDocReturnTag extends JSDocTag {
@@ -3267,6 +3269,7 @@ declare namespace ts {
32673269
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
32683270
*/
32693271
function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration>;
3272+
function getEffectiveConstraintOfTypeParameter(node: TypeParameterDeclaration): TypeNode | undefined;
32703273
}
32713274
declare namespace ts {
32723275
function isNumericLiteral(node: Node): node is NumericLiteral;

tests/baselines/reference/api/typescript.d.ts

+3
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ declare namespace ts {
548548
kind: SyntaxKind.TypeParameter;
549549
parent: DeclarationWithTypeParameterChildren | InferTypeNode;
550550
name: Identifier;
551+
/** Note: Consider calling `getEffectiveConstraintOfTypeParameter` */
551552
constraint?: TypeNode;
552553
default?: TypeNode;
553554
expression?: Expression;
@@ -1568,6 +1569,7 @@ declare namespace ts {
15681569
}
15691570
interface JSDocTemplateTag extends JSDocTag {
15701571
kind: SyntaxKind.JSDocTemplateTag;
1572+
constraint: TypeNode | undefined;
15711573
typeParameters: NodeArray<TypeParameterDeclaration>;
15721574
}
15731575
interface JSDocReturnTag extends JSDocTag {
@@ -3267,6 +3269,7 @@ declare namespace ts {
32673269
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
32683270
*/
32693271
function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration>;
3272+
function getEffectiveConstraintOfTypeParameter(node: TypeParameterDeclaration): TypeNode | undefined;
32703273
}
32713274
declare namespace ts {
32723275
function isNumericLiteral(node: Node): node is NumericLiteral;

tests/baselines/reference/jsdocTemplateTag3.errors.txt

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ tests/cases/conformance/jsdoc/a.js(14,29): error TS2339: Property 'a' does not e
22
tests/cases/conformance/jsdoc/a.js(14,35): error TS2339: Property 'b' does not exist on type 'U'.
33
tests/cases/conformance/jsdoc/a.js(21,3): error TS2345: Argument of type '{ a: number; }' is not assignable to parameter of type '{ a: number; b: string; }'.
44
Property 'b' is missing in type '{ a: number; }'.
5+
tests/cases/conformance/jsdoc/a.js(24,15): error TS2304: Cannot find name 'NoLongerAllowed'.
56
tests/cases/conformance/jsdoc/a.js(25,2): error TS1069: Unexpected token. A type parameter name was expected without curly braces.
67

78

8-
==== tests/cases/conformance/jsdoc/a.js (4 errors) ====
9+
==== tests/cases/conformance/jsdoc/a.js (5 errors) ====
910
/**
1011
* @template {{ a: number, b: string }} T,U A Comment
1112
* @template {{ c: boolean }} V uh ... are comments even supported??
@@ -37,6 +38,8 @@ tests/cases/conformance/jsdoc/a.js(25,2): error TS1069: Unexpected token. A type
3738

3839
/**
3940
* @template {NoLongerAllowed}
41+
~~~~~~~~~~~~~~~
42+
!!! error TS2304: Cannot find name 'NoLongerAllowed'.
4043
* @template T preceding line's syntax is no longer allowed
4144
~
4245
!!! error TS1069: Unexpected token. A type parameter name was expected without curly braces.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
/////**
4+
//// * @template {/**/
5+
//// */
6+
////function f() {}
7+
8+
goTo.marker("");
9+
edit.insert("n");
10+
edit.insert("u");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
// @allowJs: true
4+
// @checkJs: true
5+
// @Filename: /foo.js
6+
7+
/////**
8+
//// * Doc
9+
//// * @template {new (...args: any[]) => any} T
10+
//// * @param {T} cls
11+
//// */
12+
////function /**/myMixin(cls) {
13+
//// return class extends cls {}
14+
////}
15+
16+
verify.quickInfoAt("",
17+
`function myMixin<T extends new (...args: any[]) => any>(cls: T): {
18+
new (...args: any[]): (Anonymous class);
19+
prototype: myMixin<any>.(Anonymous class);
20+
} & T`,
21+
"Doc");

0 commit comments

Comments
 (0)