Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to extract ts type from record in ts-morph #1578

Open
programandoconro opened this issue Oct 14, 2024 · 0 comments
Open

How to extract ts type from record in ts-morph #1578

programandoconro opened this issue Oct 14, 2024 · 0 comments

Comments

@programandoconro
Copy link

I am trying to create a type tree from extracted types in a typescript file. I am using bun and ts-morph I can handle different types, like primitives, and some objects. But when trying to handle Records, I cannot get the types that compose the record. To give an example, this is the test I want the code to pass:

import { expect, test } from "bun:test";
import createTypeTree from "../src/create-type-tree";

test.only("handle complex Records", () => {
  const sourceCode = `
      type Id = string;
      type Value = {a: number, b: string}
      type MyRecord = Record<string, number>;
`;
  const result = createTypeTree("temp.ts", sourceCode);
  expect(result).toStrictEqual({
    MyRecord: { ["string"]: { a: "number", b: "string" } },
    Id: "string",
    Value: { a: "number", b: "string" },
  });
});

This is a simplified and reproducible version of my code

import { Project, Type } from "ts-morph";

type Result = Record<string, unknown>;
export default function createTypeTree(
  filePath: string,
  testCode?: string,
  configFile?: string
): Result {
  const project = new Project({
    tsConfigFilePath: configFile,
    skipAddingFilesFromTsConfig: true,
  });
  const sourceFile = !testCode
    ? project.addSourceFileAtPath(filePath)
    : project.createSourceFile(filePath, testCode);

  const result = [
    ...sourceFile?.getInterfaces(),
    ...sourceFile?.getTypeAliases(),
  ].reduce((acc, current) => {
    const name = current.getName();
    acc[name] = handleTypes(current.getType());
    return acc;
  }, {} as Result);

  return result;
}
function handleTypes(t?: Type) {
  return isPrimitive(t) ? handlePrimitive(t) : handleNotPrimitive(t);
}

function isPrimitive(t?: Type): boolean {
  const isString = t?.isString() || t?.isStringLiteral();
  const isBoolean = t?.isBoolean() || t?.isBooleanLiteral();
  const isNumber = t?.isNumber() || t?.isNumberLiteral();
  const isNullish = t?.isNullable();

  return Boolean(isNumber || isString || isBoolean || isNullish);
}

function handlePrimitive(t?: Type) {
  switch (true) {
    case t?.isNumberLiteral(): {
      return Number(t?.getText());
    }
    case t?.isBooleanLiteral(): {
      return t?.getText() === "true";
    }
    default: {
      return t?.getText();
    }
  }
}

function handleNotPrimitive(t?: Type) {
  // Here the important part. Record is object but returns {}.
  // I want to handle Records here.
  // I did not include tuples or arrays for simplicity

  if (t?.isObject()) {
    return handleObject(t);
  }

  return "Type not handled";
}

function handleObject(t?: Type) {
  const obj: Record<string, unknown> = {};
  t?.getProperties().forEach((prop) => {
    const name = prop?.isOptional() ? prop?.getName() + "?" : prop?.getName();
    const innerDeclaration = prop.getDeclarations();
    innerDeclaration.forEach((p) => {
      const innerType = p.getType();
      obj[name] = handleTypes(innerType);
    });
  });
  return obj;
}

How can I extract the types inside a record?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant