Search code examples
typescriptdocumentationintellisensejsdoctsdoc

Writing more descriptive intellisense docs for Typescript Union Types


Given the following code, when we call the baz function, the typeahead will show 'a' and 'b' as possible values.

enter image description here

However, if I want to provide additional documentation for each of those values, how would I do it? For example, if something like this is the desired behavior:

enter image description here

I thought I should give more context about what I'm trying to do and why:

Consider the following example:

const paintColor = {
  /** This color is good fo X */
  Waltz: "#d9e5d7",
  /** This color is good fo Y */
  "Indiana Clay": "#ed7c4b",
  /** This color is good fo Z */
  Odyssey: "#575b6a"
};

const locations = {
  /** There are no good school at this location*/
  City: "123 city center road",
  /** There are lots of parking at this location but it is very far*/
  West: "245 some other road"
};

interface HouseProp {
  color: keyof typeof paintColor; //"Waltz" | "Indiana Clay" | "Odyssey"
  location: keyof typeof locations; //"City" | "West"
}

const House = ({ color, location }: HouseProp) => {
  ...
};

Where House is a react component that renders a house based on the color and location props.

And this House component is used everywhere throughout the project.

With the current setup, I could use House like this:

<House color="Indiana Clay" location="City" />

The problem is, the IntelliSense can't pick up on the docs I've written as part of the code:

enter image description here

What I would like is this: enter image description here

P.S. I know that I could turn paintColor and locations into enums, and use things like this:

import House, {HouseColor, HouseLocation} from './House';
<House color={HouseColor["Indiana Clay"]} location={HouseLocation.City} />

But that component interface just isn't as nice as my original proposal.


Solution

  • You can't really annotate union members. You can, however, express your union differently — by using overloads or by choosing to use an enum instead.

    Solution #1: Function overloads

    /**
     * a is good fo X
     */
    function baz(param: 'a'): number;
    /**
     * b is an excellent choice for Y
     */
    function baz(param: 'b'): number;
    function baz(param: 'a' | 'b'): number {
      return 1;
    }
    

    Screenshot

    Solution #2: Overload as an interface

    interface Baz {
      /**
       * a is good fo X
       */
      (param: 'a'): number;
      /**
       * b is an excellent choice for Y
       */
      (param: 'b'): number;
    }
    
    const baz: Baz = (param: 'a' | 'b') => {
      return 1;
    }
    

    Screenshot

    Solution #3: Using an enum instead

    enum Foo {
      /**
       * a is good fo X
       */
      A = 'a',
      /**
       * b is an excellent choice for Y
       */
      B = 'b',
    }
    
    function baz(param: Foo) {
      return 1;
    }
    

    Screenshot

    I know that's not exactly what you'd like, but that's your second best option.