diff --git a/src/transforms/react-js-make-props-and-state-transform.ts b/src/transforms/react-js-make-props-and-state-transform.ts index 613c342..e6dd233 100644 --- a/src/transforms/react-js-make-props-and-state-transform.ts +++ b/src/transforms/react-js-make-props-and-state-transform.ts @@ -251,24 +251,22 @@ function buildInterfaceFromPropTypeObjectLiteral(objectLiteral: ts.ObjectLiteral // get d() {}, // AccessorDeclaration // } .filter(ts.isPropertyAssignment) - .filter(property => { - return ( - // Ignore children, React types have it - property.name.getText() !== 'children' && - ts.isPropertyAccessExpression(property.initializer) - ) - }) + // Ignore children, React types have it + .filter(property => property.name.getText() !== 'children') .map(propertyAssignment => { const name = propertyAssignment.name.getText(); - // We have guarantee this in the previous `filter` - const initializer = propertyAssignment.initializer as ts.PropertyAccessExpression - const typeValue = getTypeFromReactPropTypeExpression(initializer); - const isOptional = isPropTypeOptional(initializer); + const initializer = propertyAssignment.initializer; + const isRequired = isPropTypeRequired(initializer); + const typeExpression = isRequired + // We have guaranteed the type in `isPropTypeRequired()` + ? (initializer as ts.PropertyAccessExpression).expression + : initializer; + const typeValue = getTypeFromReactPropTypeExpression(typeExpression); return ts.createPropertySignature( [], name, - isOptional ? ts.createToken(ts.SyntaxKind.QuestionToken) : undefined, + isRequired ? undefined : ts.createToken(ts.SyntaxKind.QuestionToken), typeValue, undefined, ); @@ -282,51 +280,138 @@ function buildInterfaceFromPropTypeObjectLiteral(objectLiteral: ts.ObjectLiteral * * @param node React propTypes value */ -function getTypeFromReactPropTypeExpression(node: ts.PropertyAccessExpression) { - const text = node.getText().replace(/React\.PropTypes\./, ''); +function getTypeFromReactPropTypeExpression(node: ts.Expression): ts.TypeNode { let result = null; - if (/string/.test(text)) { - result = ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword); - } else if (/any/.test(text)) { - result = ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); - } else if (/array/.test(text)) { - result = ts.createArrayTypeNode(ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)); - } else if (/bool/.test(text)) { - result = ts.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword); - } else if (/number/.test(text)) { - result = ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword); - } else if (/object/.test(text)) { - result = ts.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword); - } else if (/node/.test(text)) { - result = ts.createTypeReferenceNode('React.ReactNode', []); - } else if (/element/.test(text)) { - result = ts.createTypeReferenceNode('JSX.Element', []); - } else if (/func/.test(text)) { - const arrayOfAny = ts.createParameter( - [], - [], - ts.createToken(ts.SyntaxKind.DotDotDotToken), - 'args', - undefined, - ts.createArrayTypeNode(ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)), - undefined, - ); - result = ts.createFunctionTypeNode( - [], - [arrayOfAny], - ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), - ); - } else { + if (ts.isPropertyAccessExpression(node)) { + /** + * PropTypes.array, + * PropTypes.bool, + * PropTypes.func, + * PropTypes.number, + * PropTypes.object, + * PropTypes.string, + * PropTypes.symbol, (ignore) + * PropTypes.node, + * PropTypes.element, + * PropTypes.any, + */ + const text = node.getText().replace(/React\.PropTypes\./, ''); + + if (/string/.test(text)) { + result = ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword); + } else if (/any/.test(text)) { + result = ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); + } else if (/array/.test(text)) { + result = ts.createArrayTypeNode(ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)); + } else if (/bool/.test(text)) { + result = ts.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword); + } else if (/number/.test(text)) { + result = ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword); + } else if (/object/.test(text)) { + result = ts.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword); + } else if (/node/.test(text)) { + result = ts.createTypeReferenceNode('React.ReactNode', []); + } else if (/element/.test(text)) { + result = ts.createTypeReferenceNode('JSX.Element', []); + } else if (/func/.test(text)) { + const arrayOfAny = ts.createParameter( + [], + [], + ts.createToken(ts.SyntaxKind.DotDotDotToken), + 'args', + undefined, + ts.createArrayTypeNode(ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)), + undefined, + ); + result = ts.createFunctionTypeNode( + [], + [arrayOfAny], + ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), + ); + } + } else if (ts.isCallExpression(node)) { + /** + * PropTypes.instanceOf(), (ignore) + * PropTypes.oneOf(), // only support oneOf([1, 2]), oneOf(['a', 'b']) + * PropTypes.oneOfType(), + * PropTypes.arrayOf(), + * PropTypes.objectOf(), + * PropTypes.shape(), + */ + const text = node.expression.getText(); + if (/oneOf$/.test(text)) { + const argument = node.arguments[0]; + if (ts.isArrayLiteralExpression(argument)) { + if (argument.elements.every(elm => ts.isStringLiteral(elm) || ts.isNumericLiteral(elm))) { + result = ts.createUnionTypeNode( + (argument.elements as ts.NodeArray).map(elm => + ts.createLiteralTypeNode(elm) + ), + ) + } + } + } else if (/oneOfType$/.test(text)) { + const argument = node.arguments[0]; + if (ts.isArrayLiteralExpression(argument)) { + result = ts.createUnionOrIntersectionTypeNode( + ts.SyntaxKind.UnionType, + argument.elements.map(elm => getTypeFromReactPropTypeExpression(elm)), + ); + } + } else if (/arrayOf$/.test(text)) { + const argument = node.arguments[0]; + if (argument) { + result = ts.createArrayTypeNode( + getTypeFromReactPropTypeExpression(argument) + ) + } + } else if (/objectOf$/.test(text)) { + const argument = node.arguments[0]; + if (argument) { + result = ts.createTypeLiteralNode([ + ts.createIndexSignature( + undefined, + undefined, + [ + ts.createParameter( + undefined, + undefined, + undefined, + 'key', + undefined, + ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + ) + ], + getTypeFromReactPropTypeExpression(argument), + ) + ]) + } + } else if (/shape$/.test(text)) { + const argument = node.arguments[0]; + if (ts.isObjectLiteralExpression(argument)) { + return buildInterfaceFromPropTypeObjectLiteral(argument) + } + } + } + + /** + * customProp, + * anything others + */ + if (result === null) { result = ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); } - return result; + + return result } /** - * Decide if node is optional + * Decide if node is required * @param node React propTypes member node */ -function isPropTypeOptional(node: ts.PropertyAccessExpression) { +function isPropTypeRequired(node: ts.Expression) { + if (!ts.isPropertyAccessExpression(node)) return false; + const text = node.getText().replace(/React\.PropTypes\./, ''); - return !/\.isRequired/.test(text) + return /\.isRequired/.test(text); } diff --git a/test/react-js-make-props-and-state-transform/static-proptypes-many-props/input.tsx b/test/react-js-make-props-and-state-transform/static-proptypes-many-props/input.tsx index 1e491c3..e54d2f4 100644 --- a/test/react-js-make-props-and-state-transform/static-proptypes-many-props/input.tsx +++ b/test/react-js-make-props-and-state-transform/static-proptypes-many-props/input.tsx @@ -12,6 +12,17 @@ export default class MyComponent extends React.Component { string: React.PropTypes.string, node: React.PropTypes.node, element: React.PropTypes.element, + oneOf: React.PropTypes.oneOf(['a', 'b', 'c']), + oneOfType: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.number, + ]), + arrayOf: React.PropTypes.arrayOf(React.PropTypes.string), + objectOf: React.PropTypes.objectOf(React.PropTypes.string), + shape: React.PropTypes.shape({ + color: React.PropTypes.string, + fontSize: React.PropTypes.number, + }), anyRequired: React.PropTypes.any.isRequired, arrayRequired: React.PropTypes.array.isRequired, boolRequired: React.PropTypes.bool.isRequired, @@ -21,6 +32,17 @@ export default class MyComponent extends React.Component { stringRequired: React.PropTypes.string.isRequired, nodeRequired: React.PropTypes.node.isRequired, elementRequired: React.PropTypes.element.isRequired, + oneOfRequired: React.PropTypes.oneOf(['a', 'b', 'c']).isRequired, + oneOfTypeRequired: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.number, + ]).isRequired, + arrayOfRequired: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, + objectOfRequired: React.PropTypes.objectOf(React.PropTypes.string).isRequired, + shapeRequired: React.PropTypes.shape({ + color: React.PropTypes.string, + fontSize: React.PropTypes.number.isRequired, + }).isRequired, }; render() { return
; diff --git a/test/react-js-make-props-and-state-transform/static-proptypes-many-props/output.tsx b/test/react-js-make-props-and-state-transform/static-proptypes-many-props/output.tsx index 7df6548..b7d105d 100644 --- a/test/react-js-make-props-and-state-transform/static-proptypes-many-props/output.tsx +++ b/test/react-js-make-props-and-state-transform/static-proptypes-many-props/output.tsx @@ -9,6 +9,16 @@ export default class MyComponent extends React.Component<{ string?: string; node?: React.ReactNode; element?: JSX.Element; + oneOf?: 'a' | 'b' | 'c'; + oneOfType?: string | number; + arrayOf?: string[]; + objectOf?: { + [key: string]: string; + }; + shape?: { + color?: string; + fontSize?: number; + }; anyRequired: any; arrayRequired: any[]; boolRequired: boolean; @@ -18,6 +28,16 @@ export default class MyComponent extends React.Component<{ stringRequired: string; nodeRequired: React.ReactNode; elementRequired: JSX.Element; + oneOfRequired: 'a' | 'b' | 'c'; + oneOfTypeRequired: string | number; + arrayOfRequired: string[]; + objectOfRequired: { + [key: string]: string; + }; + shapeRequired: { + color?: string; + fontSize: number; + }; }, { }> { static propTypes = { @@ -31,6 +51,17 @@ export default class MyComponent extends React.Component<{ string: React.PropTypes.string, node: React.PropTypes.node, element: React.PropTypes.element, + oneOf: React.PropTypes.oneOf(['a', 'b', 'c']), + oneOfType: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.number, + ]), + arrayOf: React.PropTypes.arrayOf(React.PropTypes.string), + objectOf: React.PropTypes.objectOf(React.PropTypes.string), + shape: React.PropTypes.shape({ + color: React.PropTypes.string, + fontSize: React.PropTypes.number, + }), anyRequired: React.PropTypes.any.isRequired, arrayRequired: React.PropTypes.array.isRequired, boolRequired: React.PropTypes.bool.isRequired, @@ -40,6 +71,17 @@ export default class MyComponent extends React.Component<{ stringRequired: React.PropTypes.string.isRequired, nodeRequired: React.PropTypes.node.isRequired, elementRequired: React.PropTypes.element.isRequired, + oneOfRequired: React.PropTypes.oneOf(['a', 'b', 'c']).isRequired, + oneOfTypeRequired: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.number, + ]).isRequired, + arrayOfRequired: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, + objectOfRequired: React.PropTypes.objectOf(React.PropTypes.string).isRequired, + shapeRequired: React.PropTypes.shape({ + color: React.PropTypes.string, + fontSize: React.PropTypes.number.isRequired, + }).isRequired, }; render() { return
; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy