Skip to content
This repository was archived by the owner on Nov 27, 2023. It is now read-only.

Commit 033a159

Browse files
committed
feat: Added union proptypes
1 parent faa9e0b commit 033a159

File tree

5 files changed

+96
-25
lines changed

5 files changed

+96
-25
lines changed

Diff for: index.ts

+38-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
import * as fs from 'fs';
22
import * as babylon from 'babylon';
33

4+
interface IASTNode {
5+
type: string;
6+
loc: Object;
7+
[name: string]: any;
8+
}
9+
10+
interface IProp {
11+
type: string;
12+
optional: boolean;
13+
}
14+
15+
interface IPropTypes {
16+
[name: string]: IProp;
17+
}
18+
419
export function cli(options: any): void {
520
const stdinCode: string[] = [];
621
process.stdin.on('readable', () => {
@@ -51,7 +66,7 @@ export function generate(name: string, code: string): string {
5166
walk(ast.program, {
5267
'ExportDefaultDeclaration': (node: any) => {
5368
let classname: string;
54-
let propTypes: any = false;
69+
let propTypes: IPropTypes = undefined;
5570
walk(node, {
5671
'ClassDeclaration': (node: any) => {
5772
classname = node.id.name;
@@ -80,7 +95,7 @@ export function generate(name: string, code: string): string {
8095
return writer.toString();
8196
}
8297

83-
function walk(node: any, handlers: any): void {
98+
function walk(node: IASTNode, handlers: any): void {
8499
if (isNode(node)) {
85100
if (typeof handlers[node.type] == 'function') {
86101
handlers[node.type](node);
@@ -98,7 +113,7 @@ function walk(node: any, handlers: any): void {
98113
}
99114
}
100115

101-
function isNode(obj: any): boolean {
116+
function isNode(obj: IASTNode): boolean {
102117
return obj && typeof obj.type != 'undefined' && typeof obj.loc != 'undefined';
103118
}
104119

@@ -107,11 +122,20 @@ function getReactPropTypeFromExpression(node: any): any {
107122
&& node.object.object.name == 'React' && node.object.property.name == 'PropTypes') {
108123
return node.property;
109124
} else if (node.type == 'CallExpression') {
110-
if (getReactPropTypeFromExpression(node.callee).name == 'arrayOf') {
111-
return {
112-
name: 'array',
113-
arrayType: getReactPropTypeFromExpression(node.arguments[0])
114-
};
125+
const callType: any = getReactPropTypeFromExpression(node.callee);
126+
switch (callType.name) {
127+
case 'arrayOf':
128+
return {
129+
name: 'array',
130+
arrayType: getTypeFromPropType(node.arguments[0]).type
131+
};
132+
case 'oneOfType':
133+
return {
134+
name: 'union',
135+
types: node.arguments[0].elements.map((element: IASTNode) => {
136+
return getTypeFromPropType(element).type;
137+
})
138+
};
115139
}
116140
}
117141
return 'undefined';
@@ -125,12 +149,7 @@ function isRequiredPropType(node: any): any {
125149
};
126150
}
127151

128-
interface IProperty {
129-
type: string;
130-
optional: boolean;
131-
}
132-
133-
export function getTypeFromPropType(node: any): IProperty {
152+
export function getTypeFromPropType(node: IASTNode): IProp {
134153
const result: any = {
135154
type: 'any',
136155
optional: true
@@ -143,8 +162,7 @@ export function getTypeFromPropType(node: any): IProperty {
143162
result.type = 'any';
144163
break;
145164
case 'array':
146-
let arrayType: any = type.arrayType || {name: 'any'};
147-
result.type = arrayType.name + '[]';
165+
result.type = (type.arrayType || 'any') + '[]';
148166
break;
149167
case 'bool':
150168
result.type = 'boolean';
@@ -167,6 +185,9 @@ export function getTypeFromPropType(node: any): IProperty {
167185
case 'element':
168186
result.type = 'React.ReactElement<any>';
169187
break;
188+
case 'union':
189+
result.type = type.types.map((unionType: string) => unionType).join('|');
190+
break;
170191
}
171192
}
172193
return result;
@@ -217,7 +238,7 @@ export class Writer {
217238
this.interface(`${name}Props`, () => {
218239
this.prop('key', 'any', true);
219240
Object.keys(props).forEach((propName: any) => {
220-
const prop: IProperty = props[propName];
241+
const prop: IProp = props[propName];
221242
this.prop(propName, prop.type, prop.optional);
222243
});
223244
});

Diff for: tests/parse-prop-types-test.ts

+46-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ describe('The PropType parser', () => {
1212
}
1313
};
1414
it('should return any on unknown PropTypes', () => {
15-
assert.deepEqual(getTypeFromPropType({}), {type: 'any', optional: true});
15+
const ast: any = {
16+
type: '',
17+
loc: {}
18+
};
19+
assert.deepEqual(getTypeFromPropType(ast), {type: 'any', optional: true});
1620
});
1721
it('should return any[] for generic array prop types', () => {
1822
const ast: any = {
@@ -148,4 +152,45 @@ describe('The PropType parser', () => {
148152
assert.equal(result.type, 'number[]');
149153
assert.equal(result.optional, true);
150154
});
155+
it('should return number|string for oneOfType([React.PropTypes.number, React.PropTypes.string]) prop types', () => {
156+
const ast: any = {
157+
type: 'CallExpression',
158+
loc: {},
159+
callee: {
160+
type: 'MemberExpression',
161+
loc: {},
162+
object: reactPropTypesMemberExpression,
163+
property: {
164+
name: 'oneOfType'
165+
}
166+
},
167+
arguments: [
168+
{
169+
type: 'ArrayExpression',
170+
loc: {},
171+
elements: [
172+
{
173+
type: 'MemberExpression',
174+
loc: {},
175+
object: reactPropTypesMemberExpression,
176+
property: {
177+
name: 'number'
178+
}
179+
},
180+
{
181+
type: 'MemberExpression',
182+
loc: {},
183+
object: reactPropTypesMemberExpression,
184+
property: {
185+
name: 'string'
186+
}
187+
}
188+
]
189+
}
190+
]
191+
};
192+
const result: any = getTypeFromPropType(ast);
193+
assert.equal(result.type, 'number|string');
194+
assert.equal(result.optional, true);
195+
});
151196
});

Diff for: tests/simple-component.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ declare module 'simple-component' {
1212
optionalString?: string;
1313
optionalNode?: React.ReactNode;
1414
optionalElement?: React.ReactElement<any>;
15+
optionalUnion?: string|number;
1516
optionalArrayOf?: number[];
1617
requiredFunc: (...args: any[]) => any;
1718
requiredAny: any;
19+
requiredUnion: any[]|boolean;
1820
requiredArrayOf: string[];
1921
}
2022

Diff for: tests/simple-component.jsx

+9-6
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@ export default class SimpleComponent extends React.Component {
1414
optionalElement: React.PropTypes.element,
1515
//optionalMessage: React.PropTypes.instanceOf(Message),
1616
//optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
17-
//optionalUnion: React.PropTypes.oneOfType([
18-
// React.PropTypes.string,
19-
// React.PropTypes.number,
20-
// React.PropTypes.instanceOf(Message)
21-
//]),
17+
optionalUnion: React.PropTypes.oneOfType([
18+
React.PropTypes.string,
19+
React.PropTypes.number
20+
]),
2221
optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
2322
//optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
2423
//optionalObjectWithShape: React.PropTypes.shape({
@@ -27,7 +26,11 @@ export default class SimpleComponent extends React.Component {
2726
//}),
2827
requiredFunc: React.PropTypes.func.isRequired,
2928
requiredAny: React.PropTypes.any.isRequired,
30-
requiredArrayOf: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
29+
requiredUnion: React.PropTypes.oneOfType([
30+
React.PropTypes.array,
31+
React.PropTypes.bool
32+
]).isRequired,
33+
requiredArrayOf: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
3134
};
3235

3336
render() {

Diff for: tslint.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
true,
6666
{
6767
"call-signature": "nospace",
68-
"index-signature": "space",
68+
"index-signature": "nospace",
6969
"parameter": "nospace",
7070
"property-declaration": "nospace",
7171
"variable-declaration": "nospace"

0 commit comments

Comments
 (0)