Skip to content

Commit ad0b725

Browse files
authored
fix: custom nullable column (#419)
* fix: custom nullable column * rename * format
1 parent 4c90cd7 commit ad0b725

File tree

3 files changed

+68
-13
lines changed

3 files changed

+68
-13
lines changed

.changeset/stale-signs-bow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ts-safeql/eslint-plugin": patch
3+
---
4+
5+
fixed an issue with nullable custom types in insert statements

packages/eslint-plugin/src/rules/check-sql.test.ts

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ const runMigrations1 = <TTypes extends Record<string, unknown>>(sql: Sql<TTypes>
144144
CREATE TABLE test_nullable_boolean (
145145
colname BOOLEAN
146146
);
147+
148+
CREATE TABLE test_nullable_timestamptz (
149+
colname TIMESTAMPTZ
150+
);
147151
`);
148152

149153
RuleTester.describe("check-sql", () => {
@@ -1225,16 +1229,48 @@ RuleTester.describe("check-sql", () => {
12251229
type Meta = { is_test: boolean; };
12261230
type Caregiver = { meta: Meta | null };
12271231
1228-
await sql<Caregiver[]>\`
1229-
SELECT
1230-
CASE WHEN caregiver.id IS NOT NULL
1231-
THEN jsonb_build_object('is_test', caregiver.first_name LIKE '%test%')
1232-
ELSE NULL
1233-
END AS meta
1234-
FROM
1235-
caregiver
1236-
\`;
1237-
`,
1232+
await sql<Caregiver[]>\`
1233+
SELECT
1234+
CASE WHEN caregiver.id IS NOT NULL
1235+
THEN jsonb_build_object('is_test', caregiver.first_name LIKE '%test%')
1236+
ELSE NULL
1237+
END AS meta
1238+
FROM
1239+
caregiver
1240+
\`;
1241+
`,
1242+
},
1243+
{
1244+
name: 'insert with custom type { timestamptz: "Instant" }',
1245+
options: withConnection(connections.withTag, {
1246+
overrides: { types: { timestamptz: "Instant" } },
1247+
}),
1248+
code: `
1249+
class Instant {}
1250+
function foo(instant: Instant | null) {
1251+
sql<{ colname: Instant | null }>\`
1252+
INSERT INTO test_nullable_timestamptz (colname)
1253+
VALUES (\${instant})
1254+
RETURNING *
1255+
\`
1256+
}
1257+
`,
1258+
},
1259+
{
1260+
name: 'insert with custom type { timestamptz: "Instant" } - property access',
1261+
options: withConnection(connections.withTag, {
1262+
overrides: { types: { timestamptz: "Instant" } },
1263+
}),
1264+
code: `
1265+
class Instant {}
1266+
function foo(x: { instant: Instant | null }) {
1267+
sql<{ colname: Instant | null }>\`
1268+
INSERT INTO test_nullable_timestamptz (colname)
1269+
VALUES (\${x.instant})
1270+
RETURNING *
1271+
\`
1272+
}
1273+
`,
12381274
},
12391275
],
12401276
invalid: [

packages/eslint-plugin/src/utils/ts-pg.utils.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,12 @@ function getPgTypeFromTsType(params: {
267267
const symbolType = checker.getTypeOfSymbolAtLocation(symbol!, node);
268268

269269
if (TSUtils.isTsUnionType(symbolType)) {
270+
// If union is X | null, continue with X
271+
const nonNullTypes = symbolType.types.filter((t) => t.flags !== ts.TypeFlags.Null);
272+
if (nonNullTypes.length === 1) {
273+
return checkType({ checker, type: nonNullTypes[0], options });
274+
}
275+
270276
return getPgTypeFromTsTypeUnion({ types: symbolType.types });
271277
}
272278

@@ -316,9 +322,17 @@ function getPgTypeFromTsType(params: {
316322
// Handle union types
317323
if (TSUtils.isTsUnionType(type)) {
318324
const matchingType = type.types.find((t) => t.flags in tsFlagToPgTypeMap);
319-
return matchingType
320-
? E.right({ kind: "cast", cast: tsFlagToPgTypeMap[matchingType.flags] })
321-
: E.left("Unsupported union type");
325+
if (matchingType) {
326+
return E.right({ kind: "cast", cast: tsFlagToPgTypeMap[matchingType.flags] });
327+
}
328+
329+
// If union is X | null, continue with X
330+
const nonNullTypes = type.types.filter((t) => t.flags !== ts.TypeFlags.Null);
331+
if (nonNullTypes.length === 1) {
332+
return checkType({ checker, type: nonNullTypes[0], options });
333+
}
334+
335+
return E.left("Unsupported union type");
322336
}
323337

324338
return checkType({ checker, type, options });

0 commit comments

Comments
 (0)