1

I'm building a code generation tool that takes in an input file of my database schema as an interface and generates smaller types from that.

Input

export interface Database {
  public: {
    Tables: {
      profiles: {
        Row: {
          first_name: string | null;
          id: string;
          last_name: string | null;
        };
      };
    };
  };
}

Expected Output

export type Profile = {
  first_name: string | null;
  id: string;
  last_name: string | null;
};

Actual Output

export type Profile = {
  first_name: string;
  id: string;
  last_name: string;
};

I'm having trouble capturing the null parts of my output when generating my types.

What I've currently got

const project = new Project({
  compilerOptions: {
    allowSyntheticDefaultImports: true,
    esModuleInterop: true,
    module: ModuleKind.ESNext,
    target: ScriptTarget.ESNext,
  },
});

const sourceFile = project.addSourceFileAtPath(typesPath);

// Find the 'Tables' type alias
const databaseInterface = sourceFile.getInterfaceOrThrow('Database');
const publicProperty = databaseInterface.getPropertyOrThrow('public');
const publicType = publicProperty.getType();

const tablesProperty = publicType
  .getApparentProperties()
  .find((property) => property.getName() === 'Tables');

const tablesType = project
  .getProgram()
  .getTypeChecker()
  .getTypeAtLocation(tablesProperty.getValueDeclarationOrThrow());
const tablesProperties = tablesType.getProperties();

const types: string[] = [];

for (const table of tablesProperties) {
  const tableName = table.getName();
  types.push(...generateTypes(table, tableName));
}

...


export function generateTypes(table: Symbol, tableName: string): string[] {
  // Get the table type
  const tableType = table.getTypeAtLocation(table.getValueDeclarationOrThrow());

  // Find the 'Row' property within the table type
  const rowProperty = tableType.getProperty('Row');

  // Get the type of the 'Row' property
  const rowType = rowProperty.getTypeAtLocation(
    rowProperty.getValueDeclarationOrThrow()
  );

  const rowTypeString = rowType.getText();

  const types: string[] = [];

  types.push(
    `export type ${toTypeName(tableName)} = ${rowTypeString};`,
    ...
  );

  return types;
}

I've tried a lot of variations regarding what I've posted above but every single time I run my generate function I can't get it to print | null for those scenarios where properties could be null.

Using ts-morph v18.

1

1 Answer 1

2

Turn on the strictNullChecks compiler option:

const project = new Project({
  compilerOptions: {
    allowSyntheticDefaultImports: true,
    esModuleInterop: true,
    module: ModuleKind.ESNext,
    target: ScriptTarget.ESNext,
    strictNullChecks: true, // <-- this
  },
});

Otherwise the type checker ignores undefined/null in union types and thinks all types are not nullable (ts-morph uses the same defaults found in the typescript compiler).

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much! I was really losing hope on what I was doing! But this did the trick!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.