Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Child class with overridden method does not have documentation from non-exported parent #2545

Open
nicfv opened this issue Apr 11, 2024 · 2 comments
Labels
bug Functionality does not match expectation

Comments

@nicfv
Copy link

nicfv commented Apr 11, 2024

Search terms

jsdoc, inheritance, override, export

The Problem

I have a parent class (not exported) with abstract methods, and a child class (exported) that overrides said abstract methods. In VS Code, my child class "inherits" the ts doc from the parent. Unfortunately, this is not the case in typedoc.

image

Expected Behavior

image

Actual Behavior

image

Steps to reproduce the bug

abstract class Parent {
    /**
     * Some function documentation
     */
    public notAbstract(): string {
        return 'hello';
    }
    /**
     * More function documentation
     */
    public abstract isAbstract(): string;
}

export class Child extends Parent {
    public override  notAbstract(): string {
        return 'foo';
    }
    public isAbstract(): string {
        return 'bar';
    }
}

Environment

  • Typedoc version: 0.25.13
  • TypeScript version: 5.4.5
  • Node.js version: 20.11.1
  • OS: Ubuntu 22.04
@nicfv nicfv added the bug Functionality does not match expectation label Apr 11, 2024
@nicfv
Copy link
Author

nicfv commented Apr 11, 2024

I forgot to mention, if I do not override notAbstract(), the documentation is successfully generated for Child (regardless if Parent is exported or not). This bug only occurs when I override something and the parent is not exported.

@Gerrit0
Copy link
Collaborator

Gerrit0 commented Apr 14, 2024

This is because TypeDoc inherits comments after converting everything, not as a part of comment discovery. Should be able to lift this code from TypeScript's services.ts to do the discovery magic... probably won't get to it this week.

function getDocumentationComment(declarations: readonly Declaration[] | undefined, checker: TypeChecker | undefined): SymbolDisplayPart[] {
    if (!declarations) return emptyArray;

    let doc = JsDoc.getJsDocCommentsFromDeclarations(declarations, checker);
    if (checker && (doc.length === 0 || declarations.some(hasJSDocInheritDocTag))) {
        const seenSymbols = new Set<Symbol>();
        for (const declaration of declarations) {
            const inheritedDocs = findBaseOfDeclaration(checker, declaration, symbol => {
                if (!seenSymbols.has(symbol)) {
                    seenSymbols.add(symbol);
                    if (declaration.kind === SyntaxKind.GetAccessor || declaration.kind === SyntaxKind.SetAccessor) {
                        return symbol.getContextualDocumentationComment(declaration, checker);
                    }
                    return symbol.getDocumentationComment(checker);
                }
            });
            // TODO: GH#16312 Return a ReadonlyArray, avoid copying inheritedDocs
            if (inheritedDocs) doc = doc.length === 0 ? inheritedDocs.slice() : inheritedDocs.concat(lineBreakPart(), doc);
        }
    }
    return doc;
}

function findBaseOfDeclaration<T>(checker: TypeChecker, declaration: Declaration, cb: (symbol: Symbol) => T[] | undefined): T[] | undefined {
    const classOrInterfaceDeclaration = declaration.parent?.kind === SyntaxKind.Constructor ? declaration.parent.parent : declaration.parent;
    if (!classOrInterfaceDeclaration) return;

    const isStaticMember = hasStaticModifier(declaration);
    return firstDefined(getAllSuperTypeNodes(classOrInterfaceDeclaration), superTypeNode => {
        const baseType = checker.getTypeAtLocation(superTypeNode);
        const type = isStaticMember && baseType.symbol ? checker.getTypeOfSymbol(baseType.symbol) : baseType;
        const symbol = checker.getPropertyOfType(type, declaration.symbol.name);
        return symbol ? cb(symbol) : undefined;
    });
}

export function getAllSuperTypeNodes(node: Node): readonly TypeNode[] {
    return isInterfaceDeclaration(node) ? getInterfaceBaseTypeNodes(node) || emptyArray :
        isClassLike(node) ? concatenate(singleElementArray(getEffectiveBaseTypeNode(node)), getEffectiveImplementsTypeNodes(node)) || emptyArray :
        emptyArray;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Functionality does not match expectation
Projects
None yet
Development

No branches or pull requests

2 participants