Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix typescript types without strict null checks #789

Merged
merged 1 commit into from May 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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>;
}