Repository: storybookjs/storybook
File: code/core/src/docs-tools/argTypes/convert/typescript/convert.ts (TypeScript)

Code before:
  }
};

export const convert = (type: TSType): SBType | void => {
  const { name, raw } = type;
  const base: any = {};

  if (typeof raw !== 'undefined') {
    base.raw = raw;
  }
  switch (type.name) {
    case 'string':
    case 'number':
    case 'symbol':
    case 'boolean': {
      return { ...base, name };
    }
    case 'Array': {
      return { ...base, name: 'array', value: type.elements.map(convert) };
    }
    case 'signature':
      return { ...base, ...convertSig(type) };
    case 'union':
      let result;
      // Filter out undefined from optional props when checking for enum
      const nonUndefinedElements = type.elements?.filter((element) => element.name !== 'undefined');
      if (nonUndefinedElements?.length > 0 && nonUndefinedElements.every((element) => element.name === 'literal')) {
        result = {
          ...base,
          name: 'enum',
          // @ts-expect-error fix types
          value: nonUndefinedElements.map((v) => parseLiteral(v.value)),
        };
      } else {
        result = { ...base, name, value: type.elements?.map(convert) };
      }
      return result;
    case 'intersection':
      return { ...base, name, value: type.elements?.map(convert) };
    default:
      return { ...base, name: 'other', value: name };
  }
};


Review comment (bug):
The fact that this throws TS errors either means that `TSType` is wrong, or that the check isn't done correctly, or both.

My preference would go towards fixing `TSType` to actually match all the inputs that can be received at runtime (so we can discover other type bugs elsewhere).

Can you also please check if `typeof element.raw` is consistently `undefined` when `element.name` is `'undefined'`? If so, then inspecting the type of `raw` (like done on line 29) could be safer.

Code after:
      });
      return {
        name: 'object',
        value: values,
      };
    default:
      throw new UnknownArgTypesError({ type, language: 'Typescript' });
  }
};

export const convert = (type: TSType): SBType | void => {
  const { name, raw } = type;
  const base: any = {};

  if (typeof raw !== 'undefined') {
    base.raw = raw;
  }
  switch (type.name) {
    case 'string':
    case 'number':
    case 'symbol':
    case 'boolean': {
      return { ...base, name };
    }
    case 'Array': {
      return { ...base, name: 'array', value: type.elements.map(convert) };
    }
    case 'signature':
      return { ...base, ...convertSig(type) };
    case 'union': {
      const nonUndefinedElements = type.elements.filter((element) => !isUndefined(element));
      const allLiterals = nonUndefinedElements.length > 0 && nonUndefinedElements.every(isLiteral);

      if (allLiterals) {
        // TypeScript can't infer from .every(), so we filter again with the type guard
        const literalElements = nonUndefinedElements.filter(isLiteral);
        return {
          ...base,
          name: 'enum',
          value: literalElements.map((element) => parseLiteral(element.value)),
        };
      }
      return { ...base, name, value: type.elements.map(convert) };
    }
    case 'intersection':
      return { ...base, name, value: type.elements.map(convert) };
    default:
      return { ...base, name: 'other', value: name };
  }
};