Type generation tool is not correctly handling nullable fields in TypeScript.

huangapple go评论63阅读模式
英文:

Type generation tool is not correctly handling nullable fields in TypeScript

问题

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.

英文:

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

得分: 2

打开 strictNullChecks 编译选项:

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

否则,类型检查器将忽略联合类型中的 undefined/null,并认为所有类型都不可为空(ts-morph 使用与 TypeScript 编译器中找到的相同默认值)。

英文:

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).

huangapple
  • 本文由 发表于 2023年4月20日 05:57:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/76059103.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定