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

Utility functions are included in output, instead of their resolved final types #1519

Closed
anzorb opened this issue Feb 25, 2021 · 9 comments
Closed
Labels
enhancement Improved functionality
Milestone

Comments

@anzorb
Copy link

anzorb commented Feb 25, 2021

Search terms

Maybe this is intended (or isn't implemented yet), but Utility type exports aren't being expanded in the output and instead show "Omit<>"...

export interface TSmarterLabelOptions {
   color: string;
   text: string; 
};

export type TOptions = Omit<TGlobalOptions, 'text'>;

Expected Behavior

I expect our clients to see the final result of the types involved in Utility functions:

Screen Shot 2021-02-25 at 11 36 14 AM

Actual Behavior

Screen Shot 2021-02-25 at 11 28 51 AM

This is especially problematic when re-exporting a type from another project and omitting a value (where we don't want them to know anything about the other project) and the end-users don't know what we're omitting from. This is actually how we use it:

import type { TGlobalOptions } from '../../another_project_by_tsconfig_reference';

export type TOptions = Omit<TGlobalOptions, 'text'>;

Environment

  • Typedoc version: 0.20.28
  • TypeScript version: 4.1.5
  • Node.js version: 12.18.4
  • OS: 10.15.7
@anzorb anzorb added the bug Functionality does not match expectation label Feb 25, 2021
@koskimas
Copy link

koskimas commented Feb 27, 2021

Some utility types are really complex. I kind of hope my utility type won't get expanded in the docs 😉

type ExtractTypeFromSelectArg<
  DB,
  TB extends keyof DB,
  S,
  A extends keyof any,
  R = RowType<DB, TB>
> = S extends `${infer SC}.${infer T}.${infer C} as ${infer RA}`
  ? RA extends A
    ? `${SC}.${T}` extends TB
      ? C extends keyof DB[`${SC}.${T}`]
        ? DB[`${SC}.${T}`][C]
        : never
      : never
    : never
  : S extends `${infer SC}.${infer T}.${infer C}`
  ? C extends A
    ? `${SC}.${T}` extends TB
      ? C extends keyof DB[`${SC}.${T}`]
        ? DB[`${SC}.${T}`][C]
        : never
      : never
    : never
  : S extends `${infer T}.${infer C} as ${infer RA}`
  ? RA extends A
    ? T extends TB
      ? C extends keyof DB[T]
        ? DB[T][C]
        : never
      : never
    : never
  : S extends `${infer T}.${infer C}`
  ? C extends A
    ? T extends TB
      ? C extends keyof DB[T]
        ? DB[T][C]
        : never
      : never
    : never
  : S extends A
  ? S extends keyof R
    ? R[S]
    : never
  : S extends AliasedRawBuilder<infer O, infer RA>
  ? RA extends A
    ? O
    : never
  : S extends (qb: any) => AliasedRawBuilder<infer O, infer RA>
  ? RA extends A
    ? O
    : never
  : S extends AliasedQueryBuilder<any, any, infer O, infer QA>
  ? QA extends A
    ? ValueType<O>
    : never
  : S extends (qb: any) => AliasedQueryBuilder<any, any, infer O, infer QA>
  ? QA extends A
    ? ValueType<O>
    : never
  : never

@anzorb
Copy link
Author

anzorb commented Feb 28, 2021

I feel like the purpose of documentation isn't to show the inner workings, but to show how one would use a library/api, etc. Is this not the purpose of this project? Can this be opt in?

@Gerrit0
Copy link
Collaborator

Gerrit0 commented Mar 1, 2021

This is a similar request to #1513.

I think this might make sense to do - with some limitations.

  1. This should only happen if the type alias has no type parameters. This looks like an interface
    type Foo = Record<"a" | "b", string>
    This does not:
     type Bar<T> = T extends string ? T : never
  2. This should only happen if the type is an object type.
    type Yes = { a: string }
    type No = { a: string } | { b: number }
  3. This should still happen if the type is an intersection type
    type Yes = { a: string } & { b: number }
  4. This should not happen for tuple types
     type No = [a: string, b: number]

Am I missing anything here? This won't catch the following, which it might be argued should be caught....

type Missed<T> = Record<"a" | "b", T>

cc: @thebestnom

@Gerrit0
Copy link
Collaborator

Gerrit0 commented Oct 18, 2022

I'm not sure this is something that I necessarily want to put in the core library - but https://github.com/mxsdev/ts-expand-type exists now, the code in which should be usable to create a plugin... putting this here so I remember the next time I look at this with an eye to implementing.

@treykasada
Copy link

@Gerrit0 What are your thoughts on having the outputted types match those shown by the language service (in tooltips and such)?

It feels like that would make for a nice overall user experience, since users would know exactly what the doc typings would be just by looking at the tooltips in their IDE.
Ideally the implementation could even leverage some compiler/language service APIs to do the type resolution, although I'm not sure if such APIs exist.

@Gerrit0
Copy link
Collaborator

Gerrit0 commented Nov 3, 2022

The language service is unfortunately terrible (at least last I looked at it... been a couple years) in terms of providing an API that can be used to correctly associate a displayed type name with the type that it actually represents. Because it just displays types to the user without go to definition, it will just stringify a type and then pass it through syntax highlighting... If that weren't the case, I think it'd be the way to go.

(Maybe this has improved now that semantic highlighting is a thing? Open to a PR resulting from an investigation!)

@spencer17x
Copy link

Are there any good solutions now? I see that vscode and idea floating window prompt both support this, can you refer to the logic of this section?like this:
image

@spencer17x
Copy link

Someone gave an answer to this issue: #2032. I have tested it and it can be used normally

@Gerrit0
Copy link
Collaborator

Gerrit0 commented Mar 25, 2023

0.24.0-beta.6 includes support for this, with a slight design change. I realized that it doesn't necessarily always make sense to treat a type alias as an interface with eagerly resolved properties, and some people may prefer keeping the constructing type, (Record<keyof Foo, string> might be better for docs if Foo has a ton of properties)

Thus, resolving the type is now opt-in with the @interface tag. For convenience, @prop and @property are supported for specifying comments on the properties of the resulting type.

An example:

/**
 * Foo docs
 */
export type Foo = {
    /**
     * Foo.a docs
     */
    a: 123;
    /**
     * Foo.b docs
     */
    b: 456;
};

/**
 * Bar docs
 * @property a Bar.a docs
 * @interface
 */
export type Bar = {
    [K in keyof Foo]: string;
};

With this setup, Bar could be equivalently defined as:

/** Bar docs */
export interface Bar {
  /** Bar.a docs */
  a: 123,
  /** Foo.b docs
  b: 456,
}

Notice that a's comment is overridden, but bs comment is still the one declared on the source type.

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

No branches or pull requests

5 participants