in packages/eui/scripts/babel/proptypes-from-ts-props/index.js [164:373]
function resolveIdentifierToPropTypes(node, state) {
const typeDefinitions = state.get('typeDefinitions');
const types = state.get('types');
const inflightResolves = state.get('inflightResolves') || new Set();
// Used to inject `href` and `onClick` props for `PropsForAnchor` and `PropsForButton` utility types
const hrefPropertySignature = types.tsPropertySignature(
types.Identifier('href'),
types.TSTypeAnnotation(types.tsStringKeyword())
);
const onClickPropertySignature = types.tsPropertySignature(
types.Identifier('onClick'),
types.tsTypeAnnotation(
types.tsTypeReference(
types.Identifier('MouseEventHandler'),
types.tsTypeParameterInstantiation([
types.tsTypeReference(types.Identifier('HTMLAnchorElement')),
])
)
)
);
hrefPropertySignature.optional = onClickPropertySignature.optional = true;
let identifier;
switch (node.type) {
case 'TSTypeReference':
identifier = node.typeName;
break;
case 'Identifier':
identifier = node;
break;
}
// resolve React.* identifiers
if (
identifier.type === 'TSQualifiedName' &&
identifier.left.name === 'React'
) {
return resolveIdentifierToPropTypes(identifier.right, state);
}
// React Component
switch (identifier.name) {
// PropTypes.element
case 'Component':
case 'ReactElement':
case 'ComponentClass':
case 'SFC':
case 'StatelessComponent':
return types.memberExpression(
types.identifier('PropTypes'),
types.identifier('element')
);
// PropTypes.node
case 'ReactChild':
case 'ReactNode':
return types.memberExpression(
types.identifier('PropTypes'),
types.identifier('node')
);
case 'ComponentType':
return types.memberExpression(
types.identifier('PropTypes'),
types.identifier('elementType')
);
case 'JSXElementConstructor':
return types.memberExpression(
types.identifier('PropTypes'),
types.identifier('func') // this is more accurately `elementType` but our peerDependency version of prop-types doesn't have it
);
}
if (identifier.name === 'Array') return resolveArrayToPropTypes(node, state);
if (identifier.name === 'Omit') return resolveOmitToPropTypes(node, state);
if (identifier.name === 'MouseEventHandler')
return buildPropTypePrimitiveExpression(types, 'func');
if (identifier.name === 'Function')
return buildPropTypePrimitiveExpression(types, 'func');
if (identifier.name === 'PropsForAnchor' && node.typeParameters != null) {
return getPropTypesForNode(
{
type: 'TSIntersectionType',
types: [
types.tsTypeLiteral([
hrefPropertySignature,
onClickPropertySignature,
]),
...node.typeParameters.params,
],
},
true,
state
);
}
if (identifier.name === 'PropsForButton' && node.typeParameters != null) {
return getPropTypesForNode(
{
type: 'TSIntersectionType',
types: [
types.tsTypeLiteral([onClickPropertySignature]),
...node.typeParameters.params,
],
},
true,
state
);
}
if (identifier.name === 'ExclusiveUnion') {
// We use ExclusiveUnion at the top level to exclusively discriminate between types
// propTypes itself must be an object so merge the union sets together as an intersection
// Any types that are optional or non-existant on one side must be optional after the union
const aPropType = getPropTypesForNode(
node.typeParameters.params[0],
true,
state
);
const bPropType = getPropTypesForNode(
node.typeParameters.params[1],
true,
state
);
const propsOnA = types.isCallExpression(aPropType)
? aPropType.arguments[0].properties
: [];
const propsOnB = types.isCallExpression(bPropType)
? bPropType.arguments[0].properties
: [];
// optional props is any prop that is optional or non-existant on one side
const optionalProps = new Set();
for (let i = 0; i < propsOnA.length; i++) {
const property = propsOnA[i];
const propertyName = property.key.name;
const isOptional = !isPropTypeRequired(types, property.value);
const existsOnB =
propsOnB.find((property) => property.key.name === propertyName) != null;
if (isOptional || !existsOnB) {
optionalProps.add(propertyName);
}
}
for (let i = 0; i < propsOnB.length; i++) {
const property = propsOnB[i];
const propertyName = property.key.name;
const isOptional = !isPropTypeRequired(types, property.value);
const existsOnA =
propsOnA.find((property) => property.key.name === propertyName) != null;
if (isOptional || !existsOnA) {
optionalProps.add(propertyName);
}
}
const propTypes = getPropTypesForNode(
{
type: 'TSIntersectionType',
types: node.typeParameters.params,
},
true,
state
);
if (types.isCallExpression(propTypes)) {
// apply the optionals
const properties = propTypes.arguments[0].properties;
for (let i = 0; i < properties.length; i++) {
const property = properties[i];
if (optionalProps.has(property.key.name)) {
property.value = makePropTypeOptional(types, property.value);
}
}
}
return propTypes;
}
if (identifier.name === 'OneOf') {
// the second type parameter is ignorable as it is a subset of the first,
// and the OneOf operation cannot be well-described by proptypes
const [sourceTypes] = node.typeParameters.params;
return getPropTypesForNode(sourceTypes, true, state);
}
// Lookup this identifier from types/interfaces defined in code
const identifierDefinition = typeDefinitions[identifier.name];
if (identifierDefinition) {
if (inflightResolves.has(identifier.name)) {
return types.memberExpression(
types.identifier('PropTypes'),
types.identifier('any')
);
}
inflightResolves.add(identifier.name);
state.set('inflightResolves', inflightResolves);
const propType = getPropTypesForNode(identifierDefinition, true, state);
inflightResolves.delete(identifier.name);
state.set('inflightResolves', inflightResolves);
return propType;
} else {
return null;
}
}