Typedoc: Nested parameter not rendered when top parameter is optional

Created on 20 Apr 2019  路  17Comments  路  Source: TypeStrong/typedoc

Expected Behavior

This snippet should render the nested parameter value correctly in the HTML output:

/**
 * @param options RootParameter
 * @param options.under NestedParameter
 */
export function under(options?: {under: boolean}) {}

More Information

The bug is caused because in strict mode, this snippet get's rendered as an union type of the form { ... } | undefined. It seems typedoc will not render the nested parameters in this case.

This test case fails also even when strict: false:

/**
 * @param options RootParameter
 * @param options.under NestedParameter
 */
export function under(options: {under: boolean} | undefined) {}

Would you accept a PR that cover this cases (I would extend it to null/both cases also ... | null, ... | undefined, ... | null | undefined).

Actual Behavior

The nested parameter should be rendered.

Steps to reproduce the bug

See https://github.com/maoueh/typedoc-bug-1020-repro

  • Define the snippet above in a file.
  • Ensure strict: true (this generates a union type from the parser point of view)
  • Inspect HTML, output is not rendered correctly.

Environment

  • Typedoc version: 0.14.2
bug

All 17 comments

That makes a lot of sense why nested keys aren't rendered when an argument is optional.

I definitely think it makes sense to render nested keys when an argument is marked as optional with ?. When the argument is explicitly unioned with null or undefined it might also make sense but I'd like to hear @aciccarello's thoughts on that behavior.

@Gerrit0 Is it even possible to discriminate between options?: {} and options: {} | undefined, seems the former being a sugar syntax for the latter, at least the --json output.json seems to imply that as options?: {} is being seen as this from the parser, which is also the same output when undefined | {}:

type: union,
types: [
  {
    "type": "intrinsic",
    "name": "undefined"
  },
  {
    "type": "reflection",
    "declaration": {
        ...
    }
  }
]

Purely from the JSON output, I guess it isn't possible. This seems like an oversight to me. options?: {} isn't quite sugar for options: {} | undefined as the latter requires explicitly passing undefined if the argument is not used.

You are right indeed, did not think about this, they are not the same in this optic.

It's really a blocker for me and I would like to make the fix. For now, I've skim between converter code and default theme doing the final rendering, still looking for the place where an object parameter is being converted to nested field, I guess there might be a recursive call somewhere.

Do you have some hints where I should look for in my hunt for a fix?

I'm not very familiar with how the theme works, it's been on my list to read through for a while. Parameters are rendered here.

Any updates? Any fixes?

I suspect #1103 may fix this, but I haven't had the time to dig into it yet.

I suspect #1103 may fix this, but I haven't had the time to dig into it yet.

Yeah, It works now.

But seems like the descriptions of nested parameters are not showing up.
Capture

/**ts
 * @param array Array
 * @param search Search Elem
 * @param opts Options
 * @param opts.start Start Index of Array
 * @param opts.end End Index of Array
 * @param opts.greaterFn Compare if first value is greater than second value
 * @param opts.equalFn Compare if two values are equal
 */
export function binarySearch<T>(
    array: T[],
    search: T,
    {
      start,
      end,
      greaterFn,
      equalFn,
    }: {
      start?: number;
      end?: number;
      greaterFn?: (a: T, b: T) => boolean;
      equalFn?: (a: T, b: T) => boolean;
    } = {
      start: 0,
      end: array.length - 1,
      greaterFn: (a, b): boolean => a > b,
      equalFn: (a, b): boolean => a === b,
    }
): number {
    // ...
}
 * @param opts Options

TypeDoc doesn't know what to do with this ^

You don't have any parameters called opts. You should instead name the param according to what is used in the function, so

 * @param start Start Index of Array

But start is inside a Object right?

Yes, but since you destructed the parameters so that it is available as start (and not opts.start) within the function, that is the parameter name recognized by TypeDoc.

Unfortunately it seems to happen with classes and interfaces too. Trying with v0.16.1, this code:

/**
 * Lorem ipsum
 */
class Foo {
    /**
     * Lorem ipsum
     */
    public bar?: {
        /**
         * Lorem ipsum
         */
        foobar: string;
    };
}

produces the same bug and foobar is not rendered.

bar

That's unfortunate :/ I thought the changes to exports would have fixed that.

Fixed in [email protected]. If you install without a lockfile it should be picked up, or v0.16.3 (out later today) will pull this in.

Turns out the information to render everything was already there (partially due to the changes done to exports), but the themes weren't updated to support it.

If you are using a custom theme that has changed the type.hbs helper, it will need to pull in changes from [email protected] to get this.


Rendering is tricky. There are cases when dealing with unions/intersections, conditional types, and function types where you have to be very careful to avoid generating an invalid type. I think I've caught all the edge cases, but it's possible I missed one.

Thank you @Gerrit0 for addressing this issue.
Although I still find some issues with it, since the type declaration is missing. Is it wanted?

For the same example above now it renders as:

foo_bar

Since foobar is documented, shouldn't "Type declaration" be rendered below? Who looks at the documentation knows that bar can be an object with a foobar property, but doesn't know what it is about.

This happens with nested properties too, for example:

/**
 * Lorem ipsum
 */
class Foo {
    /**
     * Lorem ipsum
     */
    public bar: {
        /**
         * Lorem ipsum
         */
        foobar?: {
            /**
             * Lorem ipsum
             */
            baz: string;
        };
    };

    //...
}

Render as:

baz

So baz isn't documented.

That could certainly use some improvement, feel free to open a new issue :)

I really want to completely rebuild the default theme. I think there is some major room for improvement in rendering the types in an easily understandable way, the nested lists used right now work, but get unwieldy... Unfortunately I don't think I'll get to this for a while...

Was this page helpful?
0 / 5 - 0 ratings