Skip to content

Commit

Permalink
Fix typescript types without strict null checks
Browse files Browse the repository at this point in the history
  • Loading branch information
danez committed May 1, 2023
1 parent 0a3fdb0 commit aca316c
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 33 deletions.
5 changes: 5 additions & 0 deletions .changeset/fuzzy-rice-dance.md
@@ -0,0 +1,5 @@
---
'react-docgen': patch
---

Fix TypeScript types when strict null checks are disabled
2 changes: 1 addition & 1 deletion packages/react-docgen/src/FileState.ts
Expand Up @@ -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;
Expand Down
20 changes: 10 additions & 10 deletions packages/react-docgen/src/utils/findFunctionReturn.ts
Expand Up @@ -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<T extends Node = Node> = (path: NodePath) => path is NodePath<T>;
interface TraverseState<T extends Node = Node> {
type Predicate<T extends NodePath> = (path: NodePath) => path is T;

interface TraverseState<T extends NodePath = NodePath> {
readonly predicate: Predicate<T>;
resolvedReturnPath?: NodePath<T>;
resolvedReturnPath?: T;
readonly seen: WeakSet<NodePath>;
}

Expand Down Expand Up @@ -35,11 +35,11 @@ const explodedVisitors = visitors.explode<TraverseState>({
},
});

function resolvesToFinalValue<T extends Node = Node>(
function resolvesToFinalValue<T extends NodePath>(
path: NodePath,
predicate: Predicate<T>,
seen: WeakSet<NodePath>,
): NodePath<T> | undefined {
): T | undefined {
// avoid returns with recursive function calls
if (seen.has(path)) {
return;
Expand Down Expand Up @@ -100,11 +100,11 @@ function resolvesToFinalValue<T extends Node = Node>(
* 2. Find all occurrences of return values
* For this the predicate acts more like a collector and always needs to return false
*/
function findFunctionReturnWithCache<T extends Node = Node>(
function findFunctionReturnWithCache<T extends NodePath>(
path: NodePath,
predicate: Predicate<T>,
seen: WeakSet<NodePath>,
): NodePath<T> | undefined {
): T | undefined {
let functionPath: NodePath = path;

if (functionPath.isObjectProperty()) {
Expand Down Expand Up @@ -147,9 +147,9 @@ function findFunctionReturnWithCache<T extends Node = Node>(
* 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<T extends Node = Node>(
export default function findFunctionReturn<T extends NodePath = NodePath>(
path: NodePath,
predicate: Predicate<T>,
): NodePath<T> | undefined {
): T | undefined {
return findFunctionReturnWithCache(path, predicate, new WeakSet());
}
33 changes: 24 additions & 9 deletions packages/react-docgen/src/utils/getFlowType.ts
Expand Up @@ -22,6 +22,7 @@ import type {
Identifier,
InterfaceDeclaration,
IntersectionTypeAnnotation,
Node,
NullableTypeAnnotation,
NumberLiteralTypeAnnotation,
ObjectTypeAnnotation,
Expand Down Expand Up @@ -77,18 +78,32 @@ function getFlowTypeWithRequirements(
function handleKeysHelper(
path: NodePath<GenericTypeAnnotation>,
): ElementsType | null {
let value = path.get('typeParameters').get('params')[0];
const typeParams = path.get('typeParameters');

if (!typeParams.hasNode()) {
return null;
}

let value: NodePath<Node | null | undefined> | 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<Node | null | undefined>;
}
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);

Expand Down Expand Up @@ -299,7 +314,7 @@ function handleNullableTypeAnnotation(
path: NodePath<NullableTypeAnnotation>,
typeParams: TypeParameters | null,
): TypeDescriptor | null {
const typeAnnotation = getTypeAnnotation(path);
const typeAnnotation = getTypeAnnotation<FlowType>(path);

if (!typeAnnotation) return null;

Expand All @@ -325,7 +340,7 @@ function handleFunctionTypeAnnotation(
};

path.get('params').forEach((param) => {
const typeAnnotation = getTypeAnnotation(param);
const typeAnnotation = getTypeAnnotation<FlowType>(param);

type.signature.arguments.push({
name: param.node.name ? param.node.name.name : '',
Expand All @@ -338,7 +353,7 @@ function handleFunctionTypeAnnotation(
const rest = path.get('rest');

if (rest.hasNode()) {
const typeAnnotation = getTypeAnnotation(rest);
const typeAnnotation = getTypeAnnotation<FlowType>(rest);

type.signature.arguments.push({
name: rest.node.name ? rest.node.name.name : '',
Expand Down
16 changes: 8 additions & 8 deletions packages/react-docgen/src/utils/getMethodDocumentation.ts
Expand Up @@ -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';
Expand Down Expand Up @@ -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<FlowType | TSType>(paramPath);
const typePath = getTypeAnnotation(paramPath);

if (typePath) {
if (typePath.isFlowType()) {
Expand Down Expand Up @@ -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) };
}
}
Expand Down
9 changes: 4 additions & 5 deletions 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<T = FlowType>(
path: NodePath,
export default function getTypeAnnotation<T extends Node = FlowType | TSType>(
path: NodePath<Node | null | undefined>,
): NodePath<T> | null {
if (!path.has('typeAnnotation')) return null;

Expand All @@ -20,6 +20,5 @@ export default function getTypeAnnotation<T = FlowType>(
!resultPath.isTSType()
);

// @ts-ignore
return resultPath;
return resultPath as NodePath<T>;
}

0 comments on commit aca316c

Please sign in to comment.