Skip to content

Invalid code generated if schema contains columns with names that are not valid Typescript identifiers #70

@PSimbo

Description

@PSimbo

Invalid code is generated if the database schema contains table columns whose names are not valid Typescript identifiers. Specifically, the generated interfaces contain properties that are not valid.

For example, if my schema file contains:

CREATE TABLE exampledb.example (
    "id" integer,
    "invalid-name" text
);

Then the interface generated for a query tagged with -- name: CreateExample :one is:

export interface CreateExampleRow {
    id: any;
    invalid-name: any;
}

The Typescript spec. applies the same limitations to object properties as it does to identifiers and so the solution appears to be to check whether column names are valid Typescript identifiers during generation and, if so, quote the property name in the generated code.

The following function is not perfect for determining whether a string is a valid Typescript identifier in the general case since keywords may not be used as identifiers. However, modern versions of Typescript do allow keywords to be used as property names so it's good enough for our purposes. In my test code, I added this to utlis.ts:

export function isIdentifier(str: string): boolean {
  return /^[\p{ID_Start}_$][\p{ID_Continue}_$]*$/u.test(str);
}

Then, in app.ts, I updated the import line for ./drivers/utlis to include isIdentifier and I updated the rowDecl function to look like this:

function rowDecl(
  name: string,
  driver: Driver,
  columns: Column[]
) {
  return factory.createInterfaceDeclaration(
    [factory.createToken(SyntaxKind.ExportKeyword)],
    factory.createIdentifier(name),
    undefined,
    undefined,
    columns.map((column, i) => {
      const name = colName(i, column);
      return factory.createPropertySignature(
        undefined,
        isIdentifier(name) ? factory.createIdentifier(name) : factory.createStringLiteral(name),
        undefined,
        driver.columnType(column)
      )
    })
  );
}

Following these changes, the generated interface for the example above now looks like this:

export interface CreateExampleRow {
    id: any;
    "invalid-name": any;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions