Skip to content

Commit 4c79494

Browse files
committed
refactor: enhance type handling in getPgTypeFromTsTypeUnion
Improves the type handling logic in the getPgTypeFromTsTypeUnion function by adding support for null types and refining the inference process for union types. This change ensures more accurate type resolution and better error handling for mixed types in unions, enhancing overall type inference capabilities in the ESLint plugin.
1 parent 95810f6 commit 4c79494

File tree

1 file changed

+45
-50
lines changed

1 file changed

+45
-50
lines changed

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

Lines changed: 45 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -192,26 +192,50 @@ const tsFlagToPgTypeMap: Record<number, string> = {
192192
[ts.TypeFlags.BigIntLiteral]: "bigint",
193193
};
194194

195-
function getPgTypeFromTsTypeUnion(params: { types: ts.Type[] }): E.Either<string, PgTypeStrategy> {
196-
const types = params.types.filter((t) => (t.flags & ts.TypeFlags.Null) === 0);
197-
const isStringLiterals = types.every((t) => t.flags & ts.TypeFlags.StringLiteral);
195+
function getPgTypeFromTsTypeUnion(params: {
196+
types: ts.Type[];
197+
checker: ts.TypeChecker;
198+
options: RuleOptionConnection;
199+
}): E.Either<string, PgTypeStrategy | null> {
200+
const { types, checker, options } = params;
201+
const nonNullTypes = types.filter((t) => (t.flags & ts.TypeFlags.Null) === 0);
202+
203+
if (nonNullTypes.length === 0) {
204+
return E.right(null);
205+
}
206+
207+
const isStringLiterals = nonNullTypes.every((t) => t.flags & ts.TypeFlags.StringLiteral);
198208

199209
if (isStringLiterals) {
200210
return E.right({
201211
kind: "one-of",
202-
types: types.map((t) => (t as ts.StringLiteralType).value),
212+
types: nonNullTypes.map((t) => (t as ts.StringLiteralType).value),
203213
cast: "text",
204214
});
205215
}
206216

207-
const isUnionOfTheSameType = types.every((t) => t.flags === types[0].flags);
208-
const pgType = getPgTypeFromFlags(types[0].flags);
217+
const results = nonNullTypes.map((t) => checkType({ checker, type: t, options }));
218+
const firstResult = results[0];
209219

210-
if (!isUnionOfTheSameType || pgType === undefined) {
211-
return E.left(createMixedTypesInUnionErrorMessage(types.map((t) => t.flags)));
220+
if (E.isLeft(firstResult)) {
221+
return firstResult;
212222
}
213223

214-
return E.right({ kind: "cast", cast: pgType });
224+
const firstPgType = firstResult.right;
225+
226+
for (let i = 1; i < results.length; i++) {
227+
const result = results[i];
228+
if (E.isLeft(result)) {
229+
return result;
230+
}
231+
232+
const pgType = result.right;
233+
if (pgType.cast !== firstPgType.cast) {
234+
return E.left(createMixedTypesInUnionErrorMessage(nonNullTypes.map((t) => t.flags)));
235+
}
236+
}
237+
238+
return E.right({ kind: "cast", cast: firstPgType.cast });
215239
}
216240

217241
type PgTypeStrategy =
@@ -273,21 +297,17 @@ function getPgTypeFromTsType(params: {
273297
}
274298

275299
if (symbolType.isUnion()) {
276-
// If union is X | null, continue with X
277-
const nonNullTypes = symbolType.types.filter((t) => (t.flags & ts.TypeFlags.Null) === 0);
278-
if (nonNullTypes.length === 1) {
279-
return checkType({ checker, type: nonNullTypes[0], options });
280-
}
281-
282-
return getPgTypeFromTsTypeUnion({ types: symbolType.types });
300+
return getPgTypeFromTsTypeUnion({ types: symbolType.types, checker, options });
283301
}
284302

285303
if (TSUtils.isTsArrayUnionType(checker, symbolType)) {
286304
const elementTypeUnion = symbolType.resolvedTypeArguments?.[0].types;
287305
return elementTypeUnion
288306
? pipe(
289-
getPgTypeFromTsTypeUnion({ types: elementTypeUnion }),
290-
E.map((pgType) => ({ kind: "cast", cast: `${pgType.cast}[]` })),
307+
getPgTypeFromTsTypeUnion({ types: elementTypeUnion, checker, options }),
308+
E.map((pgType) =>
309+
pgType === null ? null : { kind: "cast", cast: `${pgType.cast}[]` },
310+
),
291311
)
292312
: E.left("Invalid array union type");
293313
}
@@ -327,22 +347,7 @@ function getPgTypeFromTsType(params: {
327347

328348
// Handle union types
329349
if (type.isUnion()) {
330-
const matchingType = type.types.find((t) => {
331-
const flags = getPgTypeFromFlags(t.flags);
332-
return flags !== undefined && flags !== null;
333-
});
334-
335-
if (matchingType) {
336-
return E.right({ kind: "cast", cast: getPgTypeFromFlags(matchingType.flags)! });
337-
}
338-
339-
// If union is X | null, continue with X
340-
const nonNullTypes = type.types.filter((t) => (t.flags & ts.TypeFlags.Null) === 0);
341-
if (nonNullTypes.length === 1) {
342-
return checkType({ checker, type: nonNullTypes[0], options });
343-
}
344-
345-
return E.left("Unsupported union type");
350+
return getPgTypeFromTsTypeUnion({ types: type.types, checker, options });
346351
}
347352

348353
return checkType({ checker, type, options });
@@ -437,22 +442,12 @@ function checkType(params: {
437442

438443
// Handle union types
439444
if (type.isUnion()) {
440-
const matchingType = type.types.find((t) => {
441-
const flags = getPgTypeFromFlags(t.flags);
442-
return flags !== undefined && flags !== null;
443-
});
444-
445-
if (matchingType) {
446-
return E.right({ kind: "cast", cast: getPgTypeFromFlags(matchingType.flags)! });
447-
}
448-
449-
// If union is X | null, continue with X
450-
const nonNullTypes = type.types.filter((t) => (t.flags & ts.TypeFlags.Null) === 0);
451-
if (nonNullTypes.length === 1) {
452-
return checkType({ checker, type: nonNullTypes[0], options });
453-
}
454-
455-
return E.left("Unsupported union type");
445+
return pipe(
446+
getPgTypeFromTsTypeUnion({ types: type.types, checker, options }),
447+
E.chain((pgType) =>
448+
pgType === null ? E.left("Unsupported union type (only null)") : E.right(pgType),
449+
),
450+
);
456451
}
457452

458453
// Fallback for unsupported types

0 commit comments

Comments
 (0)