Skip to content

Commit

Permalink
Add utility to merge types
Browse files Browse the repository at this point in the history
  • Loading branch information
rvetere committed Apr 30, 2024
1 parent 33ad337 commit 79bc139
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 14 deletions.
21 changes: 19 additions & 2 deletions packages/react-docgen/src/handlers/codeTypeHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { NodePath } from '@babel/traverse';
import type { FlowType } from '@babel/types';
import type { ComponentNode } from '../resolver/index.js';
import type { Handler } from './index.js';
import mergeTSIntersectionTypes from '../utils/mergeTSIntersectionTypes.js';

function setPropDescriptor(
documentation: Documentation,
Expand Down Expand Up @@ -87,8 +88,24 @@ function setPropDescriptor(

const propDescriptor = documentation.getPropDescriptor(propName);

propDescriptor.required = !path.node.optional;
propDescriptor.tsType = type;
if (propDescriptor.tsType) {
const mergedType = mergeTSIntersectionTypes(
{
name: type.name,
required: !path.node.optional,
},
{
name: propDescriptor.tsType.name,
required: propDescriptor.required,
},
);

propDescriptor.tsType.name = mergedType.name;
propDescriptor.required = mergedType.required;
} else {
propDescriptor.tsType = type;
propDescriptor.required = !path.node.optional;
}

// We are doing this here instead of in a different handler
// to not need to duplicate the logic for checking for
Expand Down
18 changes: 6 additions & 12 deletions packages/react-docgen/src/utils/getTSType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import type {
TSParenthesizedType,
} from '@babel/types';
import { getDocblock } from './docblock.js';
import mergeTSIntersectionTypes from './mergeTSIntersectionTypes.js';

const tsTypes: Record<string, string> = {
TSAnyKeyword: 'any',
Expand Down Expand Up @@ -318,7 +319,6 @@ function handleTSIntersectionType(
});

const elementsDedup: PropertyWithKey[] = [];
const forbiddenTypes = ['unknown', 'never'];

// dedup elements
elements.forEach((element) => {
Expand All @@ -328,27 +328,21 @@ function handleTSIntersectionType(
if (hasProperties(signature)) {
signature.properties.forEach((property) => {
const existingIndex = elementsDedup.findIndex(
(e) => e.key === property.key,
({ key }) => key === property.key,
);

if (existingIndex === -1) {
elementsDedup.push(property);
} else {
// If the element is already in the array, we need to merge the properties
const existingProperty = elementsDedup[existingIndex];

if (existingProperty) {
elementsDedup[existingIndex] = {
key: property.key,
value: {
name: forbiddenTypes.includes(property.value.name)
? existingProperty.value.name
: property.value.name,
required:
property.value.required === false
? false
: existingProperty.value.required,
},
value: mergeTSIntersectionTypes(
property.value,
existingProperty.value,
),
};
}
}
Expand Down
65 changes: 65 additions & 0 deletions packages/react-docgen/src/utils/mergeTSIntersectionTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type {
TSFunctionSignatureType,
TypeDescriptor,
} from '../Documentation.js';

/**
* Merges two TSFunctionSignatureType types into one.
*
* @example
* const existingType = {
* "key": "children",
* "value": {
* "name": "ReactNode",
* "required": true,
* },
* };
* const newType = {
* "key": "children",
* "value": {
* "name": "never",
* "required": false,
* },
* };
*
* return {
* "key": "children",
* "value": {
* "name": "ReactNode",
* "required": false,
* },
* };
*/
export default (
newType: TypeDescriptor<TSFunctionSignatureType>,
existingType: TypeDescriptor<TSFunctionSignatureType>,
): TypeDescriptor<TSFunctionSignatureType> => {
const newTypeIsForbiddenToUse = ['never'].includes(newType.name);

if (newTypeIsForbiddenToUse) {
let mergedType = {
...existingType,
required:
newType.required === false || existingType.required === false
? false
: existingType.required,
};

if ('nullable' in existingType) {
mergedType = {
...mergedType,
nullable: newType.nullable === true ? true : existingType.nullable,
};
}

return mergedType;
}

return {
...newType,
required:
newType.required === false || existingType.required === false
? false
: existingType.required,
};
};

0 comments on commit 79bc139

Please sign in to comment.