function resolveIdentifierToPropTypes()

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;
  }
}