reactjs / react-docgen

A CLI and library to extract information from React component files for documentation generation purposes.
https://react-docgen.dev
MIT License
3.67k stars 296 forks source link

Better TS enum support #877

Open shilman opened 11 months ago

shilman commented 11 months ago

Reporting from https://github.com/storybookjs/storybook/pull/24165

Currently TS enums analysis is lossy:

enum ButtonType {
  Button = 'button',
  Reset = 'reset',
  Submit = 'submit',
}

type ButtonTestProps = {
  type?: ButtonType;
};

export const ButtonTest = (props: ButtonTestProps) => {
  return <button {...props}>Test</button>;
};

Produces:

          "tsType": {
            "name": "ButtonType"
          },

Compare this to a union:

type ButtonType = "button" | "reset" | "submit";

Which produces:

          "tsType": {
            "name": "union",
            "raw": "\"button\" | \"reset\" | \"submit\"",
            "elements": [
              {
                "name": "literal",
                "value": "\"button\""
              },
              {
                "name": "literal",
                "value": "\"reset\""
              },
              {
                "name": "literal",
                "value": "\"submit\""
              }
            ]
          },

It would be fantastic if react-docgen could produce something similar for enums!

rvetere commented 6 months ago

I believe this is not the case anymore...? When i test enums in our project, i got this output:

"variant": {
  "tsType": {
    "name": "union",
    "raw": "\"standard\" | \"primary\"",
    "elements": [
      {
        "name": "literal",
        "value": "\"standard\""
      },
      {
        "name": "literal",
        "value": "\"primary\""
      }
    ]
  },
  "required": false,
  "description": "The variant of the button.",
  "defaultValue": {
    "value": "\"standard\"",
    "computed": false
  }
}

the typescript type looks like this:

export type ButtonProps = {
  /** The variant of the button. */
  variant?: "standard" | "primary";
};
kasperpeulen commented 5 months ago

@shilman maybe this is actually a storybook integration problem instead. It seems that the enum is available. cc @valentinpalkovic

jtiala commented 3 days ago

the typescript type looks like this:

export type ButtonProps = {
  /** The variant of the button. */
  variant?: "standard" | "primary";
};

That prop is not an enum but a string literal union.

Docgen doesn't work for enums or string literal tuples with const assertion:

import React from "react";

type Pet1 = "cat" | "dog";

const petTupleValues = ["cat", "dog"] as const;
type PetTuple = typeof petTupleValues;
type Pet2 = PetTuple[number];

enum PetEnum {
  CAT = "cat",
  DOG = "dog",
}

type Pet3 = PetEnum;

export interface ExampleProps {
  /** String literal union. */
  unionProp?: Pet1;

  /** Tuple of string literals with const assertion. */
  tupleProp?: Pet2;

  /** Enum. */
  enumProp?: Pet3;
}

/** Example */
export const Example = ({
  unionProp = "cat",
  tupleProp = "cat",
  enumProp = PetEnum.CAT,
}: ExampleProps) => {
  const unionValue = typeof unionProp === "string" ? unionProp : "";
  const tupleValue = typeof tupleProp === "string" ? tupleProp : "";
  const enumValue = typeof enumProp === "string" ? enumProp : "";

  return (
    <ul>
      <li>Union: {unionValue}</li>
      <li>Tuple: {tupleValue}</li>
      <li>Enum: {enumValue}</li>
    </ul>
  );
};
{
  "example.tsx": [
    {
      "description": "Example",
      "methods": [],
      "displayName": "Example",
      "props": {
        "unionProp": {
          "required": false,
          "tsType": {
            "name": "union",
            "raw": "\"cat\" | \"dog\"",
            "elements": [
              {
                "name": "literal",
                "value": "\"cat\""
              },
              {
                "name": "literal",
                "value": "\"dog\""
              }
            ]
          },
          "description": "String literal union.",
          "defaultValue": {
            "value": "\"cat\"",
            "computed": false
          }
        },
        "tupleProp": {
          "required": false,
          "tsType": {
            "name": "petTupleValues[number]",
            "raw": "PetTuple[number]"
          },
          "description": "Tuple of string literals with const assertion.",
          "defaultValue": {
            "value": "\"cat\"",
            "computed": false
          }
        },
        "enumProp": {
          "required": false,
          "tsType": {
            "name": "PetEnum"
          },
          "description": "Enum.",
          "defaultValue": {
            "value": "PetEnum.CAT",
            "computed": true
          }
        }
      }
    }
  ]
}

Reproduction in Storybook here.

react-docgen-typescript analyses all of these three cases correctly.