diff --git a/.changeset/fuzzy-rice-dance.md b/.changeset/fuzzy-rice-dance.md new file mode 100644 index 00000000000..866e41e1880 --- /dev/null +++ b/.changeset/fuzzy-rice-dance.md @@ -0,0 +1,5 @@ +--- +'react-docgen': patch +--- + +Fix TypeScript types when strict null checks are disabled diff --git a/packages/react-docgen/src/FileState.ts b/packages/react-docgen/src/FileState.ts index 5b76db7163d..239a533e673 100644 --- a/packages/react-docgen/src/FileState.ts +++ b/packages/react-docgen/src/FileState.ts @@ -6,7 +6,7 @@ import babelParse from './babelParser.js'; import type { TransformOptions } from '@babel/core'; // Workaround while babel is not a proper ES module -const traverse = babelTraverse.default ?? babelTraverse; +const traverse = babelTraverse.default ?? (babelTraverse as never); export default class FileState { opts: TransformOptions; diff --git a/packages/react-docgen/src/utils/findFunctionReturn.ts b/packages/react-docgen/src/utils/findFunctionReturn.ts index ab305d795e1..492ae08201c 100644 --- a/packages/react-docgen/src/utils/findFunctionReturn.ts +++ b/packages/react-docgen/src/utils/findFunctionReturn.ts @@ -2,12 +2,12 @@ import type { NodePath } from '@babel/traverse'; import { visitors } from '@babel/traverse'; import resolveToValue from './resolveToValue.js'; import { ignore } from './traverse.js'; -import type { Node } from '@babel/types'; -type Predicate = (path: NodePath) => path is NodePath; -interface TraverseState { +type Predicate = (path: NodePath) => path is T; + +interface TraverseState { readonly predicate: Predicate; - resolvedReturnPath?: NodePath; + resolvedReturnPath?: T; readonly seen: WeakSet; } @@ -35,11 +35,11 @@ const explodedVisitors = visitors.explode({ }, }); -function resolvesToFinalValue( +function resolvesToFinalValue( path: NodePath, predicate: Predicate, seen: WeakSet, -): NodePath | undefined { +): T | undefined { // avoid returns with recursive function calls if (seen.has(path)) { return; @@ -100,11 +100,11 @@ function resolvesToFinalValue( * 2. Find all occurrences of return values * For this the predicate acts more like a collector and always needs to return false */ -function findFunctionReturnWithCache( +function findFunctionReturnWithCache( path: NodePath, predicate: Predicate, seen: WeakSet, -): NodePath | undefined { +): T | undefined { let functionPath: NodePath = path; if (functionPath.isObjectProperty()) { @@ -147,9 +147,9 @@ function findFunctionReturnWithCache( * 2. Find all occurrences of return values * For this the predicate acts more like a collector and always needs to return false */ -export default function findFunctionReturn( +export default function findFunctionReturn( path: NodePath, predicate: Predicate, -): NodePath | undefined { +): T | undefined { return findFunctionReturnWithCache(path, predicate, new WeakSet()); } diff --git a/packages/react-docgen/src/utils/getFlowType.ts b/packages/react-docgen/src/utils/getFlowType.ts index b47bc0dd8cc..35e0a4300a3 100644 --- a/packages/react-docgen/src/utils/getFlowType.ts +++ b/packages/react-docgen/src/utils/getFlowType.ts @@ -22,6 +22,7 @@ import type { Identifier, InterfaceDeclaration, IntersectionTypeAnnotation, + Node, NullableTypeAnnotation, NumberLiteralTypeAnnotation, ObjectTypeAnnotation, @@ -77,18 +78,32 @@ function getFlowTypeWithRequirements( function handleKeysHelper( path: NodePath, ): ElementsType | null { - let value = path.get('typeParameters').get('params')[0]; + const typeParams = path.get('typeParameters'); + + if (!typeParams.hasNode()) { + return null; + } + + let value: NodePath | undefined = + typeParams.get('params')[0]; + + if (!value) { + return null; + } if (value.isTypeofTypeAnnotation()) { - value = value.get('argument').get('id'); + value = value.get('argument').get('id') as NodePath< + Node | null | undefined + >; } else if (!value.isObjectTypeAnnotation()) { - value = value.get('id'); + value = value.get('id') as NodePath; } - const resolvedPath = resolveToValue(value); + + const resolvedPath = value.hasNode() ? resolveToValue(value) : value; if ( - resolvedPath && - (resolvedPath.isObjectExpression() || resolvedPath.isObjectTypeAnnotation()) + resolvedPath.isObjectExpression() || + resolvedPath.isObjectTypeAnnotation() ) { const keys = resolveObjectToNameArray(resolvedPath, true); @@ -299,7 +314,7 @@ function handleNullableTypeAnnotation( path: NodePath, typeParams: TypeParameters | null, ): TypeDescriptor | null { - const typeAnnotation = getTypeAnnotation(path); + const typeAnnotation = getTypeAnnotation(path); if (!typeAnnotation) return null; @@ -325,7 +340,7 @@ function handleFunctionTypeAnnotation( }; path.get('params').forEach((param) => { - const typeAnnotation = getTypeAnnotation(param); + const typeAnnotation = getTypeAnnotation(param); type.signature.arguments.push({ name: param.node.name ? param.node.name.name : '', @@ -338,7 +353,7 @@ function handleFunctionTypeAnnotation( const rest = path.get('rest'); if (rest.hasNode()) { - const typeAnnotation = getTypeAnnotation(rest); + const typeAnnotation = getTypeAnnotation(rest); type.signature.arguments.push({ name: rest.node.name ? rest.node.name.name : '', diff --git a/packages/react-docgen/src/utils/getMethodDocumentation.ts b/packages/react-docgen/src/utils/getMethodDocumentation.ts index a3969a44ace..3dc0162ea53 100644 --- a/packages/react-docgen/src/utils/getMethodDocumentation.ts +++ b/packages/react-docgen/src/utils/getMethodDocumentation.ts @@ -4,11 +4,9 @@ import type { ClassMethod, ClassPrivateMethod, ClassProperty, - FlowType, Function as FunctionType, ObjectMethod, ObjectProperty, - TSType, } from '@babel/types'; import { getDocblock } from './docblock.js'; import getFlowType from './getFlowType.js'; @@ -78,7 +76,7 @@ function getMethodParamsDoc(methodPath: MethodNodePath): MethodParameter[] { // Extract param types. functionExpression.get('params').forEach((paramPath) => { let type: TypeDescriptor | null = null; - const typePath = getTypeAnnotation(paramPath); + const typePath = getTypeAnnotation(paramPath); if (typePath) { if (typePath.isFlowType()) { @@ -112,13 +110,15 @@ function getMethodReturnDoc(methodPath: MethodNodePath): MethodReturn | null { const functionExpression = getMethodFunctionExpression(methodPath); if (functionExpression && functionExpression.node.returnType) { - const returnType = getTypeAnnotation( - functionExpression.get('returnType') as NodePath, - ); + const returnType = getTypeAnnotation(functionExpression.get('returnType')); - if (returnType && returnType.isFlowType()) { + if (!returnType) { + return null; + } + + if (returnType.isFlowType()) { return { type: getFlowType(returnType, null) }; - } else if (returnType) { + } else if (returnType.isTSType()) { return { type: getTSType(returnType, null) }; } } diff --git a/packages/react-docgen/src/utils/getTypeAnnotation.ts b/packages/react-docgen/src/utils/getTypeAnnotation.ts index 363bfc8f00b..3424700b150 100644 --- a/packages/react-docgen/src/utils/getTypeAnnotation.ts +++ b/packages/react-docgen/src/utils/getTypeAnnotation.ts @@ -1,12 +1,12 @@ import type { NodePath } from '@babel/traverse'; -import type { FlowType } from '@babel/types'; +import type { FlowType, Node, TSType } from '@babel/types'; /** * Gets the most inner valuable TypeAnnotation from path. If no TypeAnnotation * can be found null is returned */ -export default function getTypeAnnotation( - path: NodePath, +export default function getTypeAnnotation( + path: NodePath, ): NodePath | null { if (!path.has('typeAnnotation')) return null; @@ -20,6 +20,5 @@ export default function getTypeAnnotation( !resultPath.isTSType() ); - // @ts-ignore - return resultPath; + return resultPath as NodePath; }