function getDocgenTypeHelper()

in modules/material-parser/src/parse/ts/index.ts [108:390]


function getDocgenTypeHelper(
  checker: ts.TypeChecker,
  type: ts.Type,
  skipRequired = false,
  parentIds: number[] = [],
  isRequired = false,
): any {
  function isTuple(_type: ts.Type) {
    // @ts-ignore use internal methods
    return checker.isArrayLikeType(_type) && !checker.isArrayType(_type);
  }
  let required: boolean;
  if (isRequired !== undefined) {
    required = isRequired;
  } else {
    required = !(type.flags & SymbolFlags.Optional) || isRequired;
  }

  function makeResult(typeInfo: Json) {
    if (skipRequired) {
      return {
        raw: checker.typeToString(type),
        ...typeInfo,
      };
    } else {
      return {
        required,
        raw: checker.typeToString(type),
        ...typeInfo,
      };
    }
  }

  function getShapeFromArray(symbolArr: ts.Symbol[], _type: ts.Type) {
    const shape: Array<{
      key:
        | {
            name: string;
          }
        | string;
      value: any;
    }> = symbolArr.map((prop) => {
      const propType = checker.getTypeOfSymbolAtLocation(
        prop,
        // @ts-ignore
        prop.valueDeclaration || (prop.declarations && prop.declarations[0]) || {},
      );
      return {
        key: prop.getName(),

        value: getDocgenTypeHelper(
          checker,
          propType,
          false,
          // @ts-ignore
          getNextParentIds(parentIds, _type),
          // @ts-ignore
          !prop?.valueDeclaration?.questionToken,
        ),
      };
    });
    // @ts-ignore use internal methods
    if (checker.isArrayLikeType(_type)) {
      return shape;
    }
    if (_type.getStringIndexType()) {
      // @ts-ignore use internal methods
      if (!_type.stringIndexInfo) {
        return shape;
      }
      shape.push({
        key: {
          name: 'string',
        },
        value: getDocgenTypeHelper(
          checker,
          // @ts-ignore use internal methods
          _type.stringIndexInfo.type,
          false,
          getNextParentIds(parentIds, _type),
        ),
      });
    } else if (_type.getNumberIndexType()) {
      // @ts-ignore use internal methods
      if (!_type.numberIndexInfo) {
        return shape;
      }
      shape.push({
        key: {
          name: 'number',
        },

        value: getDocgenTypeHelper(
          checker,
          // @ts-ignore use internal methods
          _type.numberIndexInfo.type,
          false,
          getNextParentIds(parentIds, _type),
        ),
      });
    }
    return shape;
  }

  function getShape(_type: ts.Type) {
    const { symbol } = _type;
    if (symbol && symbol.members) {
      // @ts-ignore
      const props: ts.Symbol[] = Array.from(symbol.members.values());
      // if (props.length >= 20) {
      //   throw new Error('too many props');
      // }
      return getShapeFromArray(
        props.filter((prop) => prop.getName() !== '__index'),
        _type,
      );
    } else {
      // @ts-ignore
      const args = _type.resolvedTypeArguments || [];
      const props = checker.getPropertiesOfType(_type);
      // if (props.length >= 20) {
      //   throw new Error('too many props');
      // }
      const shape = getShapeFromArray(props.slice(0, args.length), _type);
      return shape;
    }
  }

  // @ts-ignore
  if (type?.kind === SyntaxKind.VoidExpression) {
    return makeResult({
      name: 'void',
      raw: 'void',
    });
  }

  const pattern = /^__global\.(.+)$/;
  // @ts-ignore
  if (parentIds.includes(type?.symbol?.id)) {
    return makeResult({
      name: 'object', // checker.typeToString(type),
    });
  }
  if (type.symbol) {
    const symbolName = getSymbolName(type.symbol);
    if (symbolName) {
      const matches = pattern.exec(symbolName);
      if (matches) {
        return makeResult({
          name: matches[1],
        });
      }
    }
  }

  if (type.flags & TypeFlags.Number) {
    return makeResult({
      name: 'number',
    });
  } else if (type.flags & TypeFlags.String) {
    return makeResult({
      name: 'string',
    });
  } else if (type.flags & TypeFlags.NumberLiteral) {
    return makeResult({
      name: 'literal',
      // @ts-ignore
      value: type.value,
    });
  } else if (type.flags & TypeFlags.Literal) {
    return makeResult({
      name: 'literal',
      value: checker.typeToString(type),
    });
  } else if (type.symbol?.flags & SymbolFlags.Enum) {
    return makeResult({
      name: 'union',
      // @ts-ignore
      value: type.types.map((t) => t.value),
    });
    // @ts-ignore
  } else if (type.flags & TypeFlags.DisjointDomains) {
    return makeResult({
      name: checker.typeToString(type),
    });
  } else if (type.flags & TypeFlags.Any) {
    return makeResult({
      name: 'any',
    });
  } else if (type.flags & TypeFlags.Union && !isComplexType(type)) {
    return makeResult({
      name: 'union',
      // @ts-ignore
      value: type.types.map((t) =>
        getDocgenTypeHelper(checker, t, true, getNextParentIds(parentIds, type))),
    });
  } else if (isComplexType(type)) {
    return makeResult({
      name: getSymbolName(type?.symbol || type?.aliasSymbol),
    });
  } else if (type.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
    if (isTuple(type)) {
      try {
        const props = getShape(type);
        return makeResult({
          name: 'tuple',
          value: props.map((p) => p.value),
        });
      } catch (e) {
        return makeResult({
          name: 'object',
        });
      }

      // @ts-ignore
    } else if (checker.isArrayType(type)) {
      return makeResult({
        name: 'Array',
        // @ts-ignore
        elements: [
          getDocgenTypeHelper(
            checker,
            (type as ExtendedType).typeArguments[0],
            false,
            getNextParentIds(parentIds, type),
          ),
        ],
      });
      // @ts-ignore
    } else if (type?.symbol?.valueDeclaration?.parameters?.length) {
      return makeResult({
        name: 'func',
        params: getFunctionParams(
          // @ts-ignore
          type?.symbol?.valueDeclaration?.parameters,
          checker,
          parentIds,
          type,
        ),
        returns: getFunctionReturns(
          checker.typeToTypeNode(type, type?.symbol?.valueDeclaration),
          checker,
          parentIds,
          type,
        ),
      });
    } else if (
      // @ts-ignore
      type?.members?.get('__call')?.declarations[0]?.symbol?.declarations[0]?.parameters?.length
    ) {
      return makeResult({
        name: 'func',
        params: getFunctionParams(
          // @ts-ignore
          type?.members?.get('__call')?.declarations[0]?.symbol?.declarations[0]?.parameters,
          checker,
          parentIds,
          type,
        ),
      });
    } else {
      try {
        const props = getShape(type);
        return makeResult({
          name: 'signature',
          type: {
            signature: {
              properties: props,
            },
          },
        });
      } catch (e) {
        return makeResult({
          name: 'object',
        });
      }
    }
  } else {
    return makeResult({
      name: 'object',
    });
  }
}