Don't store @template constraint in a TypeParameterDeclaration node (#26283)
* Don't store @template constraint in a TypeParameterDeclaration node * Code review * Update API
This commit is contained in:
parent
5efd1cb4a7
commit
a73161e9d5
|
@ -5950,7 +5950,8 @@ namespace ts {
|
|||
|
||||
/** A type parameter is thisless if its contraint is thisless, or if it has no constraint. */
|
||||
function isThislessTypeParameter(node: TypeParameterDeclaration) {
|
||||
return !node.constraint || isThislessType(node.constraint);
|
||||
const constraint = getEffectiveConstraintOfTypeParameter(node);
|
||||
return !constraint || isThislessType(constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6737,7 +6738,7 @@ namespace ts {
|
|||
}
|
||||
|
||||
function getConstraintDeclarationForMappedType(type: MappedType) {
|
||||
return type.declaration.typeParameter.constraint;
|
||||
return getEffectiveConstraintOfTypeParameter(type.declaration.typeParameter);
|
||||
}
|
||||
|
||||
function isMappedTypeWithKeyofConstraintDeclaration(type: MappedType) {
|
||||
|
@ -7874,7 +7875,7 @@ namespace ts {
|
|||
|
||||
function getConstraintDeclaration(type: TypeParameter) {
|
||||
const decl = type.symbol && getDeclarationOfKind<TypeParameterDeclaration>(type.symbol, SyntaxKind.TypeParameter);
|
||||
return decl && decl.constraint;
|
||||
return decl && getEffectiveConstraintOfTypeParameter(decl);
|
||||
}
|
||||
|
||||
function getInferredTypeParameterConstraint(typeParameter: TypeParameter) {
|
||||
|
@ -7938,7 +7939,9 @@ namespace ts {
|
|||
}
|
||||
|
||||
function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol | undefined {
|
||||
return getSymbolOfNode(getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter)!.parent);
|
||||
const tp = getDeclarationOfKind<TypeParameterDeclaration>(typeParameter.symbol, SyntaxKind.TypeParameter)!;
|
||||
const host = isJSDocTemplateTag(tp.parent) ? getHostSignatureFromJSDoc(tp.parent) : tp.parent;
|
||||
return host && getSymbolOfNode(host);
|
||||
}
|
||||
|
||||
function getTypeListId(types: ReadonlyArray<Type> | undefined) {
|
||||
|
@ -22008,7 +22011,7 @@ namespace ts {
|
|||
checkSourceElement(node.default);
|
||||
const typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node));
|
||||
if (!hasNonCircularBaseConstraint(typeParameter)) {
|
||||
error(node.constraint, Diagnostics.Type_parameter_0_has_a_circular_constraint, typeToString(typeParameter));
|
||||
error(getEffectiveConstraintOfTypeParameter(node), Diagnostics.Type_parameter_0_has_a_circular_constraint, typeToString(typeParameter));
|
||||
}
|
||||
if (!hasNonCircularTypeParameterDefault(typeParameter)) {
|
||||
error(node.default, Diagnostics.Type_parameter_0_has_a_circular_default, typeToString(typeParameter));
|
||||
|
@ -22741,7 +22744,7 @@ namespace ts {
|
|||
|
||||
const type = <MappedType>getTypeFromMappedTypeNode(node);
|
||||
const constraintType = getConstraintTypeFromMappedType(type);
|
||||
checkTypeAssignableTo(constraintType, keyofConstraintType, node.typeParameter.constraint);
|
||||
checkTypeAssignableTo(constraintType, keyofConstraintType, getEffectiveConstraintOfTypeParameter(node.typeParameter));
|
||||
}
|
||||
|
||||
function checkThisType(node: ThisTypeNode) {
|
||||
|
@ -23632,6 +23635,13 @@ namespace ts {
|
|||
checkSourceElement(node.typeExpression);
|
||||
}
|
||||
|
||||
function checkJSDocTemplateTag(node: JSDocTemplateTag): void {
|
||||
checkSourceElement(node.constraint);
|
||||
for (const tp of node.typeParameters) {
|
||||
checkSourceElement(tp);
|
||||
}
|
||||
}
|
||||
|
||||
function checkJSDocTypeTag(node: JSDocTypeTag) {
|
||||
checkSourceElement(node.typeExpression);
|
||||
}
|
||||
|
@ -25422,7 +25432,8 @@ namespace ts {
|
|||
|
||||
// If the type parameter node does not have an identical constraint as the resolved
|
||||
// type parameter at this position, we report an error.
|
||||
const sourceConstraint = source.constraint && getTypeFromTypeNode(source.constraint);
|
||||
const constraint = getEffectiveConstraintOfTypeParameter(source);
|
||||
const sourceConstraint = constraint && getTypeFromTypeNode(constraint);
|
||||
const targetConstraint = getConstraintOfTypeParameter(target);
|
||||
if (sourceConstraint) {
|
||||
// relax check if later interface augmentation has no constraint
|
||||
|
@ -26642,6 +26653,8 @@ namespace ts {
|
|||
case SyntaxKind.JSDocTypedefTag:
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
return checkJSDocTypeAliasTag(node as JSDocTypedefTag);
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
return checkJSDocTemplateTag(node as JSDocTemplateTag);
|
||||
case SyntaxKind.JSDocTypeTag:
|
||||
return checkJSDocTypeTag(node as JSDocTypeTag);
|
||||
case SyntaxKind.JSDocParameterTag:
|
||||
|
|
|
@ -475,7 +475,7 @@ namespace ts {
|
|||
case SyntaxKind.JSDocAugmentsTag:
|
||||
return visitNode(cbNode, (<JSDocAugmentsTag>node).class);
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
return visitNodes(cbNode, cbNodes, (<JSDocTemplateTag>node).typeParameters);
|
||||
return visitNode(cbNode, (<JSDocTemplateTag>node).constraint) || visitNodes(cbNode, cbNodes, (<JSDocTemplateTag>node).typeParameters);
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
if ((node as JSDocTypedefTag).typeExpression &&
|
||||
(node as JSDocTypedefTag).typeExpression!.kind === SyntaxKind.JSDocTypeExpression) {
|
||||
|
@ -7049,13 +7049,10 @@ namespace ts {
|
|||
typeParameters.push(typeParameter);
|
||||
} while (parseOptionalJsdoc(SyntaxKind.CommaToken));
|
||||
|
||||
if (constraint) {
|
||||
first(typeParameters).constraint = constraint.type;
|
||||
}
|
||||
|
||||
const result = <JSDocTemplateTag>createNode(SyntaxKind.JSDocTemplateTag, atToken.pos);
|
||||
result.atToken = atToken;
|
||||
result.tagName = tagName;
|
||||
result.constraint = constraint;
|
||||
result.typeParameters = createNodeArray(typeParameters, typeParametersPos);
|
||||
finishNode(result);
|
||||
return result;
|
||||
|
|
|
@ -759,6 +759,7 @@ namespace ts {
|
|||
kind: SyntaxKind.TypeParameter;
|
||||
parent: DeclarationWithTypeParameterChildren | InferTypeNode;
|
||||
name: Identifier;
|
||||
/** Note: Consider calling `getEffectiveConstraintOfTypeParameter` */
|
||||
constraint?: TypeNode;
|
||||
default?: TypeNode;
|
||||
|
||||
|
@ -2363,6 +2364,7 @@ namespace ts {
|
|||
|
||||
export interface JSDocTemplateTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocTemplateTag;
|
||||
constraint: TypeNode | undefined;
|
||||
typeParameters: NodeArray<TypeParameterDeclaration>;
|
||||
}
|
||||
|
||||
|
|
|
@ -1020,6 +1020,8 @@ namespace ts {
|
|||
return !isExpressionWithTypeArgumentsInClassExtendsClause(parent);
|
||||
case SyntaxKind.TypeParameter:
|
||||
return node === (<TypeParameterDeclaration>parent).constraint;
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
return node === (<JSDocTemplateTag>parent).constraint;
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
case SyntaxKind.PropertySignature:
|
||||
case SyntaxKind.Parameter:
|
||||
|
@ -5108,6 +5110,13 @@ namespace ts {
|
|||
}
|
||||
return node.typeParameters || (isInJavaScriptFile(node) ? getJSDocTypeParameterDeclarations(node) : emptyArray);
|
||||
}
|
||||
|
||||
export function getEffectiveConstraintOfTypeParameter(node: TypeParameterDeclaration): TypeNode | undefined {
|
||||
return node.constraint ? node.constraint
|
||||
: isJSDocTemplateTag(node.parent) && node === node.parent.typeParameters[0]
|
||||
? node.parent.constraint
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Simple node tests of the form `node.kind === SyntaxKind.Foo`.
|
||||
|
|
|
@ -548,6 +548,7 @@ declare namespace ts {
|
|||
kind: SyntaxKind.TypeParameter;
|
||||
parent: DeclarationWithTypeParameterChildren | InferTypeNode;
|
||||
name: Identifier;
|
||||
/** Note: Consider calling `getEffectiveConstraintOfTypeParameter` */
|
||||
constraint?: TypeNode;
|
||||
default?: TypeNode;
|
||||
expression?: Expression;
|
||||
|
@ -1568,6 +1569,7 @@ declare namespace ts {
|
|||
}
|
||||
interface JSDocTemplateTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocTemplateTag;
|
||||
constraint: TypeNode | undefined;
|
||||
typeParameters: NodeArray<TypeParameterDeclaration>;
|
||||
}
|
||||
interface JSDocReturnTag extends JSDocTag {
|
||||
|
@ -3267,6 +3269,7 @@ declare namespace ts {
|
|||
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
|
||||
*/
|
||||
function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration>;
|
||||
function getEffectiveConstraintOfTypeParameter(node: TypeParameterDeclaration): TypeNode | undefined;
|
||||
}
|
||||
declare namespace ts {
|
||||
function isNumericLiteral(node: Node): node is NumericLiteral;
|
||||
|
|
|
@ -548,6 +548,7 @@ declare namespace ts {
|
|||
kind: SyntaxKind.TypeParameter;
|
||||
parent: DeclarationWithTypeParameterChildren | InferTypeNode;
|
||||
name: Identifier;
|
||||
/** Note: Consider calling `getEffectiveConstraintOfTypeParameter` */
|
||||
constraint?: TypeNode;
|
||||
default?: TypeNode;
|
||||
expression?: Expression;
|
||||
|
@ -1568,6 +1569,7 @@ declare namespace ts {
|
|||
}
|
||||
interface JSDocTemplateTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocTemplateTag;
|
||||
constraint: TypeNode | undefined;
|
||||
typeParameters: NodeArray<TypeParameterDeclaration>;
|
||||
}
|
||||
interface JSDocReturnTag extends JSDocTag {
|
||||
|
@ -3267,6 +3269,7 @@ declare namespace ts {
|
|||
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
|
||||
*/
|
||||
function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration>;
|
||||
function getEffectiveConstraintOfTypeParameter(node: TypeParameterDeclaration): TypeNode | undefined;
|
||||
}
|
||||
declare namespace ts {
|
||||
function isNumericLiteral(node: Node): node is NumericLiteral;
|
||||
|
|
|
@ -2,10 +2,11 @@ tests/cases/conformance/jsdoc/a.js(14,29): error TS2339: Property 'a' does not e
|
|||
tests/cases/conformance/jsdoc/a.js(14,35): error TS2339: Property 'b' does not exist on type 'U'.
|
||||
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; }'.
|
||||
Property 'b' is missing in type '{ a: number; }'.
|
||||
tests/cases/conformance/jsdoc/a.js(24,15): error TS2304: Cannot find name 'NoLongerAllowed'.
|
||||
tests/cases/conformance/jsdoc/a.js(25,2): error TS1069: Unexpected token. A type parameter name was expected without curly braces.
|
||||
|
||||
|
||||
==== tests/cases/conformance/jsdoc/a.js (4 errors) ====
|
||||
==== tests/cases/conformance/jsdoc/a.js (5 errors) ====
|
||||
/**
|
||||
* @template {{ a: number, b: string }} T,U A Comment
|
||||
* @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
|
|||
|
||||
/**
|
||||
* @template {NoLongerAllowed}
|
||||
~~~~~~~~~~~~~~~
|
||||
!!! error TS2304: Cannot find name 'NoLongerAllowed'.
|
||||
* @template T preceding line's syntax is no longer allowed
|
||||
~
|
||||
!!! error TS1069: Unexpected token. A type parameter name was expected without curly braces.
|
||||
|
|
10
tests/cases/fourslash/editTemplateConstraint.ts
Normal file
10
tests/cases/fourslash/editTemplateConstraint.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
/////**
|
||||
//// * @template {/**/
|
||||
//// */
|
||||
////function f() {}
|
||||
|
||||
goTo.marker("");
|
||||
edit.insert("n");
|
||||
edit.insert("u");
|
21
tests/cases/fourslash/quickInfoTemplateTag.ts
Normal file
21
tests/cases/fourslash/quickInfoTemplateTag.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @Filename: /foo.js
|
||||
|
||||
/////**
|
||||
//// * Doc
|
||||
//// * @template {new (...args: any[]) => any} T
|
||||
//// * @param {T} cls
|
||||
//// */
|
||||
////function /**/myMixin(cls) {
|
||||
//// return class extends cls {}
|
||||
////}
|
||||
|
||||
verify.quickInfoAt("",
|
||||
`function myMixin<T extends new (...args: any[]) => any>(cls: T): {
|
||||
new (...args: any[]): (Anonymous class);
|
||||
prototype: myMixin<any>.(Anonymous class);
|
||||
} & T`,
|
||||
"Doc");
|
Loading…
Reference in a new issue