mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Adding support for @implements. (#36292)
* Adding support for @implements. * Fixed code review issues for @implements, added some more tests. * Fixed declaration emit for @interface * Improved getImplementsTypes to not cache the results since it is only used once. * Removed unnecessary checks from getImplementsTypes
This commit is contained in:
+35
-2
@@ -5889,9 +5889,13 @@ namespace ts {
|
||||
const typeParamDecls = map(localParams, p => typeParameterToDeclaration(p, context));
|
||||
const classType = getDeclaredTypeOfClassOrInterface(symbol);
|
||||
const baseTypes = getBaseTypes(classType);
|
||||
const implementsTypes = getImplementsTypes(classType);
|
||||
const staticType = getTypeOfSymbol(symbol);
|
||||
const staticBaseType = getBaseConstructorTypeOfClass(staticType as InterfaceType);
|
||||
const heritageClauses = !length(baseTypes) ? undefined : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))];
|
||||
const heritageClauses = [
|
||||
...!length(baseTypes) ? [] : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
|
||||
...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeBaseType(b, staticBaseType, localName)))]
|
||||
];
|
||||
const symbolProps = getPropertiesOfType(classType);
|
||||
const publicSymbolProps = filter(symbolProps, s => {
|
||||
const valueDecl = s.valueDeclaration;
|
||||
@@ -8251,6 +8255,26 @@ namespace ts {
|
||||
return type.resolvedBaseConstructorType;
|
||||
}
|
||||
|
||||
function getImplementsTypes(type: InterfaceType): BaseType[] {
|
||||
let resolvedImplementsTypes: BaseType[] = emptyArray;
|
||||
for (const declaration of type.symbol.declarations) {
|
||||
const implementsTypeNodes = getEffectiveImplementsTypeNodes(declaration as ClassLikeDeclaration);
|
||||
if (!implementsTypeNodes) continue;
|
||||
for (const node of implementsTypeNodes) {
|
||||
const implementsType = getTypeFromTypeNode(node);
|
||||
if (implementsType !== errorType) {
|
||||
if (resolvedImplementsTypes === emptyArray) {
|
||||
resolvedImplementsTypes = [<ObjectType>implementsType];
|
||||
}
|
||||
else {
|
||||
resolvedImplementsTypes.push(implementsType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return resolvedImplementsTypes;
|
||||
}
|
||||
|
||||
function getBaseTypes(type: InterfaceType): BaseType[] {
|
||||
if (!type.resolvedBaseTypes) {
|
||||
if (type.objectFlags & ObjectFlags.Tuple) {
|
||||
@@ -30339,6 +30363,13 @@ namespace ts {
|
||||
checkSignatureDeclaration(node);
|
||||
}
|
||||
|
||||
function checkJSDocImplementsTag(node: JSDocImplementsTag): void {
|
||||
const classLike = getJSDocHost(node);
|
||||
if (!isClassDeclaration(classLike) && !isClassExpression(classLike)) {
|
||||
error(classLike, Diagnostics.JSDoc_0_is_not_attached_to_a_class, idText(node.tagName));
|
||||
return;
|
||||
}
|
||||
}
|
||||
function checkJSDocAugmentsTag(node: JSDocAugmentsTag): void {
|
||||
const classLike = getJSDocHost(node);
|
||||
if (!isClassDeclaration(classLike) && !isClassExpression(classLike)) {
|
||||
@@ -32609,7 +32640,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
const implementedTypeNodes = getClassImplementsHeritageClauseElements(node);
|
||||
const implementedTypeNodes = getEffectiveImplementsTypeNodes(node);
|
||||
if (implementedTypeNodes) {
|
||||
for (const typeRefNode of implementedTypeNodes) {
|
||||
if (!isEntityNameExpression(typeRefNode.expression)) {
|
||||
@@ -33829,6 +33860,8 @@ namespace ts {
|
||||
return checkImportType(<ImportTypeNode>node);
|
||||
case SyntaxKind.JSDocAugmentsTag:
|
||||
return checkJSDocAugmentsTag(node as JSDocAugmentsTag);
|
||||
case SyntaxKind.JSDocImplementsTag:
|
||||
return checkJSDocImplementsTag(node as JSDocImplementsTag);
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
case SyntaxKind.JSDocEnumTag:
|
||||
|
||||
@@ -1519,8 +1519,9 @@ namespace ts {
|
||||
case SyntaxKind.JSDocThisTag:
|
||||
case SyntaxKind.JSDocEnumTag:
|
||||
return emitJSDocSimpleTypedTag(node as JSDocTypeTag);
|
||||
case SyntaxKind.JSDocImplementsTag:
|
||||
case SyntaxKind.JSDocAugmentsTag:
|
||||
return emitJSDocAugmentsTag(node as JSDocAugmentsTag);
|
||||
return emitJSDocHeritageTag(node as JSDocImplementsTag | JSDocAugmentsTag);
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
return emitJSDocTemplateTag(node as JSDocTemplateTag);
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
@@ -3468,7 +3469,7 @@ namespace ts {
|
||||
emitJSDocComment(tag.comment);
|
||||
}
|
||||
|
||||
function emitJSDocAugmentsTag(tag: JSDocAugmentsTag) {
|
||||
function emitJSDocHeritageTag(tag: JSDocImplementsTag | JSDocAugmentsTag) {
|
||||
emitJSDocTagName(tag.tagName);
|
||||
writeSpace();
|
||||
writePunctuation("{");
|
||||
|
||||
@@ -479,6 +479,9 @@ namespace ts {
|
||||
visitNode(cbNode, (<JSDocPropertyLikeTag>node).name));
|
||||
case SyntaxKind.JSDocAuthorTag:
|
||||
return visitNode(cbNode, (node as JSDocTag).tagName);
|
||||
case SyntaxKind.JSDocImplementsTag:
|
||||
return visitNode(cbNode, (node as JSDocTag).tagName) ||
|
||||
visitNode(cbNode, (<JSDocImplementsTag>node).class);
|
||||
case SyntaxKind.JSDocAugmentsTag:
|
||||
return visitNode(cbNode, (node as JSDocTag).tagName) ||
|
||||
visitNode(cbNode, (<JSDocAugmentsTag>node).class);
|
||||
@@ -6999,6 +7002,9 @@ namespace ts {
|
||||
case "author":
|
||||
tag = parseAuthorTag(start, tagName, margin);
|
||||
break;
|
||||
case "implements":
|
||||
tag = parseImplementsTag(start, tagName);
|
||||
break;
|
||||
case "augments":
|
||||
case "extends":
|
||||
tag = parseAugmentsTag(start, tagName);
|
||||
@@ -7355,6 +7361,13 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function parseImplementsTag(start: number, tagName: Identifier): JSDocImplementsTag {
|
||||
const result = <JSDocImplementsTag>createNode(SyntaxKind.JSDocImplementsTag, start);
|
||||
result.tagName = tagName;
|
||||
result.class = parseExpressionWithTypeArgumentsForAugments();
|
||||
return finishNode(result);
|
||||
}
|
||||
|
||||
function parseAugmentsTag(start: number, tagName: Identifier): JSDocAugmentsTag {
|
||||
const result = <JSDocAugmentsTag>createNode(SyntaxKind.JSDocAugmentsTag, start);
|
||||
result.tagName = tagName;
|
||||
|
||||
@@ -471,6 +471,7 @@ namespace ts {
|
||||
JSDocSignature,
|
||||
JSDocTag,
|
||||
JSDocAugmentsTag,
|
||||
JSDocImplementsTag,
|
||||
JSDocAuthorTag,
|
||||
JSDocClassTag,
|
||||
JSDocPublicTag,
|
||||
@@ -1986,7 +1987,7 @@ namespace ts {
|
||||
|
||||
export interface ExpressionWithTypeArguments extends NodeWithTypeArguments {
|
||||
kind: SyntaxKind.ExpressionWithTypeArguments;
|
||||
parent: HeritageClause | JSDocAugmentsTag;
|
||||
parent: HeritageClause | JSDocAugmentsTag | JSDocImplementsTag;
|
||||
expression: LeftHandSideExpression;
|
||||
}
|
||||
|
||||
@@ -2660,6 +2661,11 @@ namespace ts {
|
||||
class: ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression };
|
||||
}
|
||||
|
||||
export interface JSDocImplementsTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocImplementsTag;
|
||||
class: ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression };
|
||||
}
|
||||
|
||||
export interface JSDocAuthorTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocAuthorTag;
|
||||
}
|
||||
|
||||
@@ -2807,15 +2807,20 @@ namespace ts {
|
||||
return heritageClause && heritageClause.types.length > 0 ? heritageClause.types[0] : undefined;
|
||||
}
|
||||
|
||||
export function getClassImplementsHeritageClauseElements(node: ClassLikeDeclaration) {
|
||||
const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ImplementsKeyword);
|
||||
return heritageClause ? heritageClause.types : undefined;
|
||||
export function getEffectiveImplementsTypeNodes(node: ClassLikeDeclaration): undefined | readonly ExpressionWithTypeArguments[]{
|
||||
if(isInJSFile(node)) {
|
||||
return getJSDocImplementsTags(node).map(n => n.class);
|
||||
}
|
||||
else {
|
||||
const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ImplementsKeyword);
|
||||
return heritageClause?.types;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the node in an `extends` or `implements` clause of a class or interface. */
|
||||
export function getAllSuperTypeNodes(node: Node): readonly TypeNode[] {
|
||||
return isInterfaceDeclaration(node) ? getInterfaceBaseTypeNodes(node) || emptyArray :
|
||||
isClassLike(node) ? concatenate(singleElementArray(getEffectiveBaseTypeNode(node)), getClassImplementsHeritageClauseElements(node)) || emptyArray :
|
||||
isClassLike(node) ? concatenate(singleElementArray(getEffectiveBaseTypeNode(node)), getEffectiveImplementsTypeNodes(node)) || emptyArray :
|
||||
emptyArray;
|
||||
}
|
||||
|
||||
|
||||
@@ -671,6 +671,11 @@ namespace ts {
|
||||
return getFirstJSDocTag(node, isJSDocAugmentsTag);
|
||||
}
|
||||
|
||||
/** Gets the JSDoc implements tags for the node if present */
|
||||
export function getJSDocImplementsTags(node: Node): readonly JSDocImplementsTag[] {
|
||||
return getAllJSDocTags(node, isJSDocImplementsTag);
|
||||
}
|
||||
|
||||
/** Gets the JSDoc class tag for the node if present */
|
||||
export function getJSDocClassTag(node: Node): JSDocClassTag | undefined {
|
||||
return getFirstJSDocTag(node, isJSDocClassTag);
|
||||
@@ -787,7 +792,12 @@ namespace ts {
|
||||
return find(getJSDocTags(node), predicate);
|
||||
}
|
||||
|
||||
/** Gets all JSDoc tags of a specified kind, or undefined if not present. */
|
||||
/** Gets all JSDoc tags that match a specified predicate */
|
||||
export function getAllJSDocTags<T extends JSDocTag>(node: Node, predicate: (tag: JSDocTag) => tag is T): readonly T[] {
|
||||
return getJSDocTags(node).filter(predicate);
|
||||
}
|
||||
|
||||
/** Gets all JSDoc tags of a specified kind */
|
||||
export function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): readonly JSDocTag[] {
|
||||
return getJSDocTags(node).filter(doc => doc.kind === kind);
|
||||
}
|
||||
@@ -1582,6 +1592,10 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.JSDocAugmentsTag;
|
||||
}
|
||||
|
||||
export function isJSDocImplementsTag(node: Node): node is JSDocImplementsTag {
|
||||
return node.kind === SyntaxKind.JSDocImplementsTag;
|
||||
}
|
||||
|
||||
export function isJSDocClassTag(node: Node): node is JSDocClassTag {
|
||||
return node.kind === SyntaxKind.JSDocClassTag;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace ts.codefix {
|
||||
getCodeActions(context) {
|
||||
const { sourceFile, span } = context;
|
||||
const classDeclaration = getClass(sourceFile, span.start);
|
||||
return mapDefined<ExpressionWithTypeArguments, CodeFixAction>(getClassImplementsHeritageClauseElements(classDeclaration), implementedTypeNode => {
|
||||
return mapDefined<ExpressionWithTypeArguments, CodeFixAction>(getEffectiveImplementsTypeNodes(classDeclaration), implementedTypeNode => {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(context, implementedTypeNode, sourceFile, classDeclaration, t, context.preferences));
|
||||
return changes.length === 0 ? undefined : createCodeFixAction(fixId, changes, [Diagnostics.Implement_interface_0, implementedTypeNode.getText(sourceFile)], fixId, Diagnostics.Implement_all_unimplemented_interfaces);
|
||||
});
|
||||
@@ -21,7 +21,7 @@ namespace ts.codefix {
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const classDeclaration = getClass(diag.file, diag.start);
|
||||
if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) {
|
||||
for (const implementedTypeNode of getClassImplementsHeritageClauseElements(classDeclaration)!) {
|
||||
for (const implementedTypeNode of getEffectiveImplementsTypeNodes(classDeclaration)!) {
|
||||
addMissingDeclarations(context, implementedTypeNode, diag.file, classDeclaration, changes, context.preferences);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +129,8 @@ namespace ts.JsDoc {
|
||||
function getCommentText(tag: JSDocTag): string | undefined {
|
||||
const { comment } = tag;
|
||||
switch (tag.kind) {
|
||||
case SyntaxKind.JSDocImplementsTag:
|
||||
return withNode((tag as JSDocImplementsTag).class);
|
||||
case SyntaxKind.JSDocAugmentsTag:
|
||||
return withNode((tag as JSDocAugmentsTag).class);
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
|
||||
Reference in New Issue
Block a user