Skip to content

Commit

Permalink
change nullable generic to type union (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
Newbie012 committed Oct 2, 2022
1 parent 7bf6f6a commit fbdfb61
Show file tree
Hide file tree
Showing 13 changed files with 35 additions and 34 deletions.
6 changes: 6 additions & 0 deletions .changeset/cuddly-maps-press.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@ts-safeql/eslint-plugin": patch
"@ts-safeql/generate": patch
---

Change autofix fromNullable generic (e.g. `Nullable<string>`) to type union format (e.g. string | null)
4 changes: 2 additions & 2 deletions demos/basic-migrations-raw/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Db, Nullable } from "@ts-safeql-demos/shared/client";
import { Db } from "@ts-safeql-demos/shared/client";
import postgres from "postgres";

export function check(client: Db) {
const sql = postgres();

client.queryOne<{ id: number; post_id: Nullable<number>; body: Nullable<string> }>(sql`
client.queryOne<{ id: number; post_id: number | null; body: string | null }>(sql`
SELECT * FROM comments
`);
}
6 changes: 2 additions & 4 deletions demos/big-project/src/original.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import postgres from "postgres";

type Nullable<T> = T | null;

interface Starship {
id: number;
name: string;
captain_id: Nullable<number>;
captain_id: number | null;
}

export function check() {
const sql = postgres();

sql<{ id: number }[]>`SELECT id FROM starship`;
sql<{ id: number; name: string; captain_id: Nullable<number> }[]>`SELECT * FROM starship`;
sql<{ id: number; name: string; captain_id: number | null }[]>`SELECT * FROM starship`;
sql<Starship[]>`SELECT * FROM starship`;
}
6 changes: 3 additions & 3 deletions demos/multi-connections/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Db, Nullable } from "@ts-safeql-demos/shared/client";
import { Db } from "@ts-safeql-demos/shared/client";
import postgres from "postgres";

export function check(client1: Db, client2: Db) {
const sql = postgres();

// client 1 points to acme/migrations1/
client1.queryOne<{ post_id: Nullable<number> }>(sql`SELECT post_id FROM comments`);
client1.queryOne<{ post_id: number | null }>(sql`SELECT post_id FROM comments`);

// client 2 points to acme/migrations2/
client2.queryOne<{ name: Nullable<string> }>(sql`SELECT name FROM chat_rooms`);
client2.queryOne<{ name: string | null }>(sql`SELECT name FROM chat_rooms`);
}
8 changes: 3 additions & 5 deletions demos/prisma/src/prisma.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Prisma, PrismaClient } from "@prisma/client";
import assert from "assert";

type Nullable<T> = T | null;

import { config } from "dotenv";
import path from "path";

Expand All @@ -20,9 +18,9 @@ async function run() {

test(`SELECT * FROM "User"`, async () => {
return assert.deepEqual(
await prisma.$queryRaw<
{ id: number; createdAt: Date; email: string; name: Nullable<string> }[]
>(Prisma.sql`SELECT * FROM "User"`),
await prisma.$queryRaw<{ id: number; createdAt: Date; email: string; name: string | null }[]>(
Prisma.sql`SELECT * FROM "User"`
),
await prisma.user.findMany()
);
});
Expand Down
4 changes: 1 addition & 3 deletions demos/prisma/src/prisma.ts-test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Prisma, PrismaClient, PrismaPromise } from "@prisma/client";
import { assert, IsExact } from "conditional-type-checks";

type Nullable<T> = T | null;

function _test() {
const prisma = new PrismaClient();

Expand All @@ -16,7 +14,7 @@ function _test() {

() => {
const raw = prisma.$queryRaw<
{ id: number; createdAt: Date; email: string; name: Nullable<string> }[]
{ id: number; createdAt: Date; email: string; name: string | null }[]
>(Prisma.sql`SELECT * FROM "User"`);
const orm = prisma.user.findMany();

Expand Down
1 change: 0 additions & 1 deletion demos/shared/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,4 @@ export function createClient() {
return { query, queryOne };
}

export type Nullable<T> = T | null;
export type Db = ReturnType<typeof createClient>;
4 changes: 2 additions & 2 deletions docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ to be an array of objects, you can use the following:
::: tip EXAMPLES

- `"${type}[]"` will transform the type to an array of the type.
- `["Nullable", "Maybe"]` will replace `Nullable` with `Maybe` in the type.
- `["${type}[]", ["Nullable", "Maybe"]]` will do both
- `["colname", "x_colname"]` will replace `colname` with `x_colname` in the type.
- `["${type}[]", ["colname", x_colname"]]` will do both

:::

Expand Down
4 changes: 2 additions & 2 deletions packages/eslint-plugin/src/rules/check-sql.rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ const baseSchema = z.object({
*
* For example:
* - `"${type}[]"` will transform the type to an array
* - `["Nullable", "Maybe"]` will replace `Nullable` with `Maybe` in the type
* - `["${type}[]", ["Nullable", "Maybe"]]` will do both
* - `["colname", "x_colname"]` will replace `colname` with `x_colname` in the type.
* - `["${type}[]", ["colname", x_colname"]]` will do both
*/
transform: z
.union([z.string(), z.array(z.union([z.string(), z.tuple([z.string(), z.string()])]))])
Expand Down
18 changes: 10 additions & 8 deletions packages/eslint-plugin/src/rules/check-sql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ RuleTester.describe("check-sql", () => {
filename,
options: withConnection(connections.base),
code: `
const result = conn.query<{ id: number; first_name: string; middle_name: Nullable<string>; last_name: string; }>(sql\`
const result = conn.query<{ id: number; first_name: string; middle_name: string | null; last_name: string; }>(sql\`
select * from caregiver
\`);
`,
Expand All @@ -148,7 +148,7 @@ RuleTester.describe("check-sql", () => {
filename,
options: withConnection(connections.base),
code: `
const result = conn.query<{ caregiver_id: number; agency_id: Nullable<number>; }>(sql\`
const result = conn.query<{ caregiver_id: number; agency_id: number | null; }>(sql\`
select
caregiver.id as caregiver_id,
agency.id as agency_id
Expand Down Expand Up @@ -352,18 +352,20 @@ RuleTester.describe("check-sql", () => {
code: "const result = conn.query<{ id: number; }[]>(sql`select id from caregiver`);",
},
{
name: "transform as [['Nullable', 'Maybe']]",
name: "transform as [['middle_name', 'x_middle_name']]",
filename,
options: withConnection(connections.base, { transform: [["Nullable", "Maybe"]] }),
code: "const result = conn.query<{ middle_name: Maybe<string>; }>(sql`select middle_name from caregiver`);",
options: withConnection(connections.base, {
transform: [["middle_name", "x_middle_name"]],
}),
code: "const result = conn.query<{ x_middle_name: string | null; }>(sql`select middle_name from caregiver`);",
},
{
name: "transform as ['${type}[]', ['Nullable', 'Maybe']]",
name: "transform as ['${type}[]', ['middle_name', 'x_middle_name']]",
filename,
options: withConnection(connections.base, {
transform: ["${type}[]", ["Nullable", "Maybe"]],
transform: ["${type}[]", ["middle_name", "x_middle_name"]],
}),
code: "const result = conn.query<{ middle_name: Maybe<string>; }[]>(sql`select middle_name from caregiver`);",
code: "const result = conn.query<{ x_middle_name: string | null; }[]>(sql`select middle_name from caregiver`);",
},
],
invalid: [],
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin/src/rules/check-sql.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function transformType(typeString: string, typeReplacer: TypeTransformer[number]
* @param transform could be either:
* - a string that has ${type} in it,
* - an array of tuples that behave as [valueToBeReplaced, typeToReplaceWith]
* - an array that has a mix of the above (such as ["${type}[]", ["Nullable", "Maybe"]])
* - an array that has a mix of the above (such as ["${type}[]", ["colname", "x_colname"]])
*/
export function withTransformType(result: GenerateResult, transform?: TypeTransformer) {
if (transform === undefined || result.result === null) {
Expand Down
2 changes: 1 addition & 1 deletion packages/generate/src/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ test("select with left join should return all cols from left join as nullable",
FROM caregiver
LEFT JOIN caregiver_agency ON caregiver.id = caregiver_agency.caregiver_id
`,
expected: `{ caregiver_id: number; assoc_id: Nullable<number>; }`,
expected: `{ caregiver_id: number; assoc_id: number | null; }`,
});
});

Expand Down
4 changes: 2 additions & 2 deletions packages/generate/src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ function mapColumnAnalysisResultsToTypeLiteral(params: {
}

function buildInterfacePropertyValue(params: { key: string; value: string; isNullable: boolean }) {
return `${params.key}: ${params.isNullable ? `Nullable<${params.value}>` : params.value}`;
return `${params.key}: ${params.isNullable ? `${params.value} | null` : params.value}`;
}

function mapColumnAnalysisResultToPropertySignature(params: {
Expand All @@ -178,7 +178,7 @@ function mapColumnAnalysisResultToPropertySignature(params: {
}) {
if ("introspected" in params.col) {
const tsType = params.typesMap[params.col.introspected.colType];
const value = params.col.introspected.colNotNull ? tsType : `Nullable<${tsType}>`;
const value = params.col.introspected.colNotNull ? tsType : `${tsType} | null`;
const isFromLeftJoin = params.leftTables.includes(params.col.introspected.tableOid);
const key = params.col.described.name ?? params.col.introspected.colName;

Expand Down

1 comment on commit fbdfb61

@vercel
Copy link

@vercel vercel bot commented on fbdfb61 Oct 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.