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

Allow documenting callable objects as being variables of their declared type (rather than a function+namespace with duplicated docs and no type declaration/link) #2881

Closed
pjeby opened this issue Feb 28, 2025 · 3 comments
Milestone

Comments

@pjeby
Copy link
Contributor

pjeby commented Feb 28, 2025

Search Terms

functions, variables, callable objects

Problem

I'm currently working on documenting a library that exports instances of this type. The type includes two call signatures, which causes typedoc to process exports of this type through convertVariableAsFunction, stripping the type information and copying all the documentation from the (external) type to the other project's output. This generates tons of duplicate documentation that also entirely obscures the fact the export is a Signal<Whatever>. So I'd like them to be rendered as variables, not functions with extra properties and methods.

Suggested Solution

Don't convertVariableAsFunction if a variable's type is an interface, type alias, class, etc. It should only be used where the type is implicit (and therefore a one-off), since an explicit declaration means the signature or property/method docs have a common definition that can be linked to (instead of inlining copies of the docs).

Another possibility would be to add a new tag, e.g. @variable, or perhaps overload the meaning of another tag, like @useDeclaredType.

@pjeby pjeby changed the title Callable objects of a type are not documented as being of that type Allow documenting callable objects as being variables of their declared type (rather than a function+namespace with duplicated docs and no type declaration/link) Feb 28, 2025
@Gerrit0
Copy link
Collaborator

Gerrit0 commented Mar 1, 2025

Ick! Doing this breaks a few test cases...

// #2307 - user probably wants a function
export const all: {
    <T>(fn: (item: T) => boolean, iterator: Iterable<T>): boolean;
    <T>(fn: (item: T) => boolean): (iterator: Iterable<T>) => boolean;
} = () => false as any;

// #2432 - function
export const bug: {
    (): { foo: string };
    foo: typeof foo;
    bar: 42;
} = Object.assign(bugInner, {
    foo,
    bar: 42 as const,
});

// #2432 - function
export const bug2 = Object.assign(bugInner, {
    foo,
    bar: 42 as const,
});

// #2755 - user wanted a function here
export interface MultiCallSignature {
    /** A */
    (): string;
    /** B */
    (x: string): string;
}

export const Callable: MultiCallSignature = () => "";

I think it's still worth making a change. I plan on introducing @function (in general, "magic" features should be opted into, not out of), and retaining the conversion of variables as functions by default if there is no type declaration.

This will probably annoy some of the React.FC people, but frankly, they're explicitly deciding that their function isn't a function, but a variable anyways... (and you shouldn't use React.FC)

Gerrit0 added a commit that referenced this issue Mar 1, 2025
@Gerrit0 Gerrit0 added this to the v0.28.0 milestone Mar 1, 2025
@pjeby
Copy link
Contributor Author

pjeby commented Mar 1, 2025

Just tried it in the beta, and this is almost what I was asking for -- I was hoping that declarations like these would work:

export const allViews = cached(() => unchangedIf(getLeaves().map(leaf => leaf.view)))

i.e. detecting based on the fact that cached() returns a named type (i.e. Signal). That is, treat as a variable if the inferred or declared type is a named interface, class, or type alias (that is not being expanded inline).

(As it happens, this rule would not break the test cases you gave, other than MultiCallSignature.)

But the current approach is still usable, as it allows explicit control either way, it just requires a redundant declaration right now. An explicit @variable tag would avoid the need to write out explicit type declarations for cases like these.

@Gerrit0
Copy link
Collaborator

Gerrit0 commented Mar 1, 2025

That type of check is very fragile -- TypeScript's rules for when something is named vs not is not at all stable.

That test case does imply to me that TypeDoc's check should be even more conservative -- only converting as a function if the initializer is a function declaration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants