mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Template tag allows specification of constraints (#24600)
* Parse (and mostly support) template tag constraints A bunch of tests hit the asserts I added though. * Messy version is finished. Need to add a few tests * Refactor to be smaller * Small refactor + Add one test * Another test * Minor cleanup * Fix error reporting on type parameters on ctors * Simplify syntax of `@template` tag This is a breaking change, but in my sample, nobody except webpack used the erroneous syntax. I need to improve the error message, so jsdocTemplateTag3 currently fails to remind me of that. * Better error message for template tag * Fix fourslash baselines * Another fourslash update * Address PR comments * Simplify getEffectiveTypeParameterDeclarations Make checkGrammarConstructorTypeParameters do a little more work
This commit is contained in:
@@ -28421,9 +28421,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
function checkGrammarConstructorTypeParameters(node: ConstructorDeclaration) {
|
||||
const typeParameters = getEffectiveTypeParameterDeclarations(node);
|
||||
if (isNodeArray(typeParameters)) {
|
||||
const { pos, end } = typeParameters;
|
||||
const jsdocTypeParameters = isInJavaScriptFile(node) && getJSDocTypeParameterDeclarations(node);
|
||||
if (node.typeParameters || jsdocTypeParameters && jsdocTypeParameters.length) {
|
||||
const { pos, end } = node.typeParameters || jsdocTypeParameters && jsdocTypeParameters[0] || node;
|
||||
return grammarErrorAtPos(node, pos, end - pos, Diagnostics.Type_parameters_cannot_appear_on_a_constructor_declaration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,6 +219,10 @@
|
||||
"category": "Error",
|
||||
"code": 1068
|
||||
},
|
||||
"Unexpected token. A type parameter name was expected without curly braces.": {
|
||||
"category": "Error",
|
||||
"code": 1069
|
||||
},
|
||||
"'{0}' modifier cannot appear on a type member.": {
|
||||
"category": "Error",
|
||||
"code": 1070
|
||||
|
||||
+14
-21
@@ -6993,29 +6993,27 @@ namespace ts {
|
||||
}
|
||||
|
||||
function parseTemplateTag(atToken: AtToken, tagName: Identifier): JSDocTemplateTag | undefined {
|
||||
if (some(tags, isJSDocTemplateTag)) {
|
||||
parseErrorAt(tagName.pos, scanner.getTokenPos(), Diagnostics._0_tag_already_specified, tagName.escapedText);
|
||||
// the template tag looks like '@template {Constraint} T,U,V'
|
||||
let constraint: JSDocTypeExpression | undefined;
|
||||
if (token() === SyntaxKind.OpenBraceToken) {
|
||||
constraint = parseJSDocTypeExpression();
|
||||
skipWhitespace();
|
||||
}
|
||||
|
||||
// Type parameter list looks like '@template T,U,V'
|
||||
const typeParameters = [];
|
||||
const typeParametersPos = getNodePos();
|
||||
|
||||
while (true) {
|
||||
const typeParameter = <TypeParameterDeclaration>createNode(SyntaxKind.TypeParameter);
|
||||
const name = parseJSDocIdentifierNameWithOptionalBraces();
|
||||
skipWhitespace();
|
||||
if (!name) {
|
||||
parseErrorAtPosition(scanner.getStartPos(), 0, Diagnostics.Identifier_expected);
|
||||
if (!tokenIsIdentifierOrKeyword(token())) {
|
||||
parseErrorAtCurrentToken(Diagnostics.Unexpected_token_A_type_parameter_name_was_expected_without_curly_braces);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
typeParameter.name = name;
|
||||
typeParameter.name = parseJSDocIdentifierName()!;
|
||||
skipWhitespace();
|
||||
finishNode(typeParameter);
|
||||
|
||||
typeParameters.push(typeParameter);
|
||||
|
||||
if (token() === SyntaxKind.CommaToken) {
|
||||
// need to look for more type parameters
|
||||
nextJSDocToken();
|
||||
skipWhitespace();
|
||||
}
|
||||
@@ -7024,6 +7022,10 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
if (constraint) {
|
||||
first(typeParameters).constraint = constraint.type;
|
||||
}
|
||||
|
||||
const result = <JSDocTemplateTag>createNode(SyntaxKind.JSDocTemplateTag, atToken.pos);
|
||||
result.atToken = atToken;
|
||||
result.tagName = tagName;
|
||||
@@ -7032,15 +7034,6 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseJSDocIdentifierNameWithOptionalBraces(): Identifier | undefined {
|
||||
const parsedBrace = parseOptional(SyntaxKind.OpenBraceToken);
|
||||
const res = parseJSDocIdentifierName();
|
||||
if (parsedBrace) {
|
||||
parseExpected(SyntaxKind.CloseBraceToken);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function nextJSDocToken(): JsDocSyntaxKind {
|
||||
return currentToken = scanner.scanJSDocToken();
|
||||
}
|
||||
|
||||
@@ -3164,21 +3164,18 @@ namespace ts {
|
||||
}
|
||||
if (isJSDocTypeAlias(node)) {
|
||||
Debug.assert(node.parent.kind === SyntaxKind.JSDocComment);
|
||||
const templateTags = flatMap(filter(node.parent.tags, isJSDocTemplateTag), tag => tag.typeParameters) as ReadonlyArray<TypeParameterDeclaration>;
|
||||
const templateTagNodes = templateTags as NodeArray<TypeParameterDeclaration>;
|
||||
templateTagNodes.pos = templateTagNodes.length > 0 ? first(templateTagNodes).pos : node.pos;
|
||||
templateTagNodes.end = templateTagNodes.length > 0 ? last(templateTagNodes).end : node.end;
|
||||
templateTagNodes.hasTrailingComma = false;
|
||||
return templateTagNodes;
|
||||
return flatMap(node.parent.tags, tag => isJSDocTemplateTag(tag) ? tag.typeParameters : undefined) as ReadonlyArray<TypeParameterDeclaration>;
|
||||
}
|
||||
return node.typeParameters || (isInJavaScriptFile(node) ? getJSDocTypeParameterDeclarations(node) : emptyArray);
|
||||
}
|
||||
|
||||
export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration> {
|
||||
// template tags are only available when a typedef isn't already using them
|
||||
const tag = find(getJSDocTags(node), (tag): tag is JSDocTemplateTag =>
|
||||
isJSDocTemplateTag(tag) && !(tag.parent.kind === SyntaxKind.JSDocComment && tag.parent.tags!.some(isJSDocTypeAlias)));
|
||||
return (tag && tag.typeParameters) || emptyArray;
|
||||
return flatMap(getJSDocTags(node), tag => isNonTypeAliasTemplate(tag) ? tag.typeParameters : undefined);
|
||||
}
|
||||
|
||||
/** template tags are only available when a typedef isn't already using them */
|
||||
function isNonTypeAliasTemplate(tag: JSDocTag): tag is JSDocTemplateTag {
|
||||
return isJSDocTemplateTag(tag) && !(tag.parent.kind === SyntaxKind.JSDocComment && tag.parent.tags!.some(isJSDocTypeAlias));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user