Skip to content

Commit 38e2bc0

Browse files
Remo VetereRemo Vetere
authored andcommitted
Finalize merge logic and add more tests
1 parent 27e5138 commit 38e2bc0

5 files changed

Lines changed: 99 additions & 29 deletions

File tree

packages/react-docgen/src/handlers/__tests__/__snapshots__/codeTypeHandler-test.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1655,7 +1655,7 @@ exports[`codeTypeHandler > stateless TS component and variable type takes preced
16551655
"description": "",
16561656
"required": true,
16571657
"tsType": {
1658-
"name": "string",
1658+
"name": "number | string",
16591659
},
16601660
},
16611661
}

packages/react-docgen/src/handlers/codeTypeHandler.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ function setPropDescriptor(
8181
return;
8282
}
8383
const type = getTSType(typeAnnotation, typeParams);
84-
8584
const propName = getPropertyName(path);
8685

8786
if (!propName) return;
@@ -90,14 +89,14 @@ function setPropDescriptor(
9089

9190
if (propDescriptor.tsType) {
9291
const mergedType = mergeTSIntersectionTypes(
93-
{
94-
name: type.name,
95-
required: !path.node.optional,
96-
},
9792
{
9893
name: propDescriptor.tsType.name,
9994
required: propDescriptor.required,
10095
},
96+
{
97+
name: type.name,
98+
required: !path.node.optional,
99+
},
101100
);
102101

103102
propDescriptor.tsType.name = mergedType.name;
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { describe, expect, test } from 'vitest';
2+
import mergeTSIntersectionTypes from '../mergeTSIntersectionTypes.js';
3+
4+
describe('mergeTSIntersectionTypes', () => {
5+
test('it merges two types correctly', () => {
6+
const mergedType = mergeTSIntersectionTypes(
7+
{
8+
name: 'string',
9+
required: true,
10+
},
11+
{
12+
name: 'number',
13+
required: true,
14+
},
15+
);
16+
17+
expect(mergedType).toEqual({
18+
name: 'string | number',
19+
required: true,
20+
});
21+
});
22+
23+
test('it ignores types of "never"', () => {
24+
const mergedType = mergeTSIntersectionTypes(
25+
{
26+
name: 'string',
27+
required: true,
28+
},
29+
{
30+
name: 'never',
31+
required: true,
32+
},
33+
);
34+
35+
expect(mergedType).toEqual({
36+
name: 'string',
37+
required: true,
38+
});
39+
});
40+
41+
test('if one of the types is "unknown", it overrides all other types', () => {
42+
const mergedType = mergeTSIntersectionTypes(
43+
{
44+
name: 'string',
45+
required: true,
46+
},
47+
{
48+
name: 'unknown',
49+
required: true,
50+
},
51+
);
52+
53+
expect(mergedType).toEqual({
54+
name: 'unknown',
55+
required: true,
56+
});
57+
});
58+
59+
test('if one of the types is NOT required, the merged one is NOT required too', () => {
60+
const mergedType = mergeTSIntersectionTypes(
61+
{
62+
name: 'string',
63+
required: true,
64+
},
65+
{
66+
name: 'number',
67+
required: false,
68+
},
69+
);
70+
71+
expect(mergedType).toEqual({
72+
name: 'string | number',
73+
required: false,
74+
});
75+
});
76+
});

packages/react-docgen/src/utils/getTSType.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,8 @@ function handleTSIntersectionType(
340340
elementsDedup[existingIndex] = {
341341
key: property.key,
342342
value: mergeTSIntersectionTypes(
343-
property.value,
344343
existingProperty.value,
344+
property.value,
345345
),
346346
};
347347
}

packages/react-docgen/src/utils/mergeTSIntersectionTypes.ts

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,35 +31,30 @@ import type {
3131
* };
3232
*/
3333
export default (
34-
newType: TypeDescriptor<TSFunctionSignatureType>,
3534
existingType: TypeDescriptor<TSFunctionSignatureType>,
35+
newType: TypeDescriptor<TSFunctionSignatureType>,
3636
): TypeDescriptor<TSFunctionSignatureType> => {
37-
const newTypeIsForbiddenToUse = ['never'].includes(newType.name);
37+
const required =
38+
newType.required === false || existingType.required === false
39+
? false
40+
: existingType.required;
3841

39-
if (newTypeIsForbiddenToUse) {
40-
let mergedType = {
41-
...existingType,
42-
required:
43-
newType.required === false || existingType.required === false
44-
? false
45-
: existingType.required,
46-
};
42+
const existingTypesArray = existingType.name.split('|').map((t) => t.trim());
43+
const existingTypes = new Set(existingTypesArray);
4744

48-
if ('nullable' in existingType) {
49-
mergedType = {
50-
...mergedType,
51-
nullable: newType.nullable === true ? true : existingType.nullable,
52-
};
53-
}
45+
if (!['never'].includes(newType.name)) {
46+
existingTypes.add(newType.name);
47+
}
5448

55-
return mergedType;
49+
if (existingType.name === 'unknown' || newType.name === 'unknown') {
50+
return {
51+
name: 'unknown',
52+
required,
53+
};
5654
}
5755

5856
return {
59-
...newType,
60-
required:
61-
newType.required === false || existingType.required === false
62-
? false
63-
: existingType.required,
57+
name: Array.from(existingTypes).join(' | '),
58+
required,
6459
};
6560
};

0 commit comments

Comments
 (0)