diff --git a/code/core/src/docs-tools/argTypes/convert/typescript/convert.ts b/code/core/src/docs-tools/argTypes/convert/typescript/convert.ts index 3beaae0a3542..c66504aee88a 100644 --- a/code/core/src/docs-tools/argTypes/convert/typescript/convert.ts +++ b/code/core/src/docs-tools/argTypes/convert/typescript/convert.ts @@ -4,6 +4,13 @@ import type { SBType } from 'storybook/internal/types'; import { parseLiteral } from '../utils'; import type { TSSigType, TSType } from './types'; +// Type guards for narrowing TSType discriminant unions +type TSLiteralType = Extract; +type TSUndefinedType = Extract; + +const isLiteral = (type: TSType): type is TSLiteralType => type.name === 'literal'; +const isUndefined = (type: TSType): type is TSUndefinedType => type.name === 'undefined'; + const convertSig = (type: TSSigType) => { switch (type.type) { case 'function': @@ -41,21 +48,23 @@ export const convert = (type: TSType): SBType | void => { } case 'signature': return { ...base, ...convertSig(type) }; - case 'union': - let result; - if (type.elements?.every((element) => element.name === 'literal')) { - result = { + 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', - // @ts-expect-error fix types - value: type.elements?.map((v) => parseLiteral(v.value)), + value: literalElements.map((element) => parseLiteral(element.value)), }; - } else { - result = { ...base, name, value: type.elements?.map(convert) }; } - return result; + return { ...base, name, value: type.elements.map(convert) }; + } case 'intersection': - return { ...base, name, value: type.elements?.map(convert) }; + return { ...base, name, value: type.elements.map(convert) }; default: return { ...base, name: 'other', value: name }; } diff --git a/code/core/src/docs-tools/argTypes/convert/typescript/types.ts b/code/core/src/docs-tools/argTypes/convert/typescript/types.ts index 79df2e812c00..ba4f02a090b8 100644 --- a/code/core/src/docs-tools/argTypes/convert/typescript/types.ts +++ b/code/core/src/docs-tools/argTypes/convert/typescript/types.ts @@ -9,7 +9,7 @@ type TSArgType = TSType; type TSCombinationType = TSBaseType & { name: 'union' | 'intersection'; - elements?: TSType[]; + elements: TSType[]; }; type TSFuncSigType = TSBaseType & { @@ -33,7 +33,12 @@ type TSObjectSigType = TSBaseType & { }; type TSScalarType = TSBaseType & { - name: 'any' | 'boolean' | 'number' | 'void' | 'string' | 'symbol' | 'literal'; + name: 'any' | 'boolean' | 'number' | 'void' | 'string' | 'symbol' | 'undefined'; +}; + +type TSLiteralType = TSBaseType & { + name: 'literal'; + value: string; }; type TSArrayType = TSBaseType & { @@ -43,4 +48,4 @@ type TSArrayType = TSBaseType & { export type TSSigType = TSObjectSigType | TSFuncSigType; -export type TSType = TSScalarType | TSCombinationType | TSSigType | TSArrayType; +export type TSType = TSScalarType | TSLiteralType | TSCombinationType | TSSigType | TSArrayType;