From 0ca3b015bfeaec906139422891e370d4c71ab0e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20Koskim=C3=A4ki?= Date: Thu, 30 Dec 2021 18:54:13 +0200 Subject: [PATCH] remove the magical UnionToIntersection type --- package-lock.json | 4 +- package.json | 2 +- src/parser/reference-parser.ts | 9 ++--- src/parser/select-parser.ts | 12 +++--- src/query-builder/on-conflict-builder.ts | 6 ++- src/util/type-utils.ts | 23 ++++++------ test/src/test-setup.ts | 4 +- test/typings/index.test-d.ts | 47 ++++++++++++++++++++++-- 8 files changed, 74 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4d70b4f26..77817777a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "kysely", - "version": "0.12.2", + "version": "0.14.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "kysely", - "version": "0.12.2", + "version": "0.14.0", "license": "MIT", "dependencies": { "@types/node": "^16.11.9" diff --git a/package.json b/package.json index 745b5795c..38927325c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kysely", - "version": "0.14.0", + "version": "0.14.1", "description": "Type safe SQL query builder", "repository": { "type": "git", diff --git a/src/parser/reference-parser.ts b/src/parser/reference-parser.ts index 45a3eaacf..e8659c0f1 100644 --- a/src/parser/reference-parser.ts +++ b/src/parser/reference-parser.ts @@ -11,7 +11,7 @@ import { import { AnyColumn, AnyColumnWithTable, - RowType, + ExtractColumnType, ValueType, } from '../util/type-utils.js' import { RawBuilder } from '../raw-builder/raw-builder.js' @@ -58,8 +58,7 @@ export type ExtractTypeFromReferenceExpression< type ExtractTypeFromStringReference< DB, TB extends keyof DB, - RE extends string, - R = RowType + RE extends string > = RE extends `${infer SC}.${infer T}.${infer C}` ? `${SC}.${T}` extends TB ? C extends keyof DB[`${SC}.${T}`] @@ -72,8 +71,8 @@ type ExtractTypeFromStringReference< ? DB[T][C] : never : never - : RE extends keyof R - ? R[RE] + : RE extends AnyColumn + ? ExtractColumnType : PrimitiveValue export function parseReferenceExpressionOrList( diff --git a/src/parser/select-parser.ts b/src/parser/select-parser.ts index 5cb06dac7..6ab245869 100644 --- a/src/parser/select-parser.ts +++ b/src/parser/select-parser.ts @@ -10,6 +10,7 @@ import { AnyAliasedColumnWithTable, AnyColumn, AnyColumnWithTable, + ExtractColumnType, RowType, ValueType, } from '../util/type-utils.js' @@ -126,8 +127,7 @@ type ExtractTypeFromStringSelectExpression< DB, TB extends keyof DB, SE extends string, - A extends keyof any, - R = RowType + A extends keyof any > = SE extends `${infer SC}.${infer T}.${infer C} as ${infer RA}` ? RA extends A ? `${SC}.${T}` extends TB @@ -146,8 +146,8 @@ type ExtractTypeFromStringSelectExpression< : never : SE extends `${infer C} as ${infer RA}` ? RA extends A - ? C extends keyof R - ? R[C] + ? C extends AnyColumn + ? ExtractColumnType : never : never : SE extends `${infer SC}.${infer T}.${infer C}` @@ -167,8 +167,8 @@ type ExtractTypeFromStringSelectExpression< : never : never : SE extends A - ? SE extends keyof R - ? R[SE] + ? SE extends AnyColumn + ? ExtractColumnType : never : never diff --git a/src/query-builder/on-conflict-builder.ts b/src/query-builder/on-conflict-builder.ts index 2a2f9d4d0..e5763776a 100644 --- a/src/query-builder/on-conflict-builder.ts +++ b/src/query-builder/on-conflict-builder.ts @@ -18,7 +18,6 @@ import { MutationObject, parseUpdateObject, } from '../parser/update-set-parser.js' -import { RawBuilder } from '../raw-builder/raw-builder.js' import { freeze } from '../util/object-utils.js' import { preventAwait } from '../util/prevent-await.js' import { AnyColumn, AnyRawBuilder } from '../util/type-utils.js' @@ -308,7 +307,10 @@ export class OnConflictBuilder */ doUpdateSet( updates: MutationObject - ): OnConflictUpdateBuilder, TB | 'excluded'> { + ): OnConflictUpdateBuilder< + { [K in keyof DB | 'excluded']: K extends keyof DB ? DB[K] : DB[TB] }, + TB | 'excluded' + > { return new OnConflictUpdateBuilder({ ...this.#props, onConflictNode: OnConflictNode.cloneWith(this.#props.onConflictNode, { diff --git a/src/util/type-utils.ts b/src/util/type-utils.ts index 53477a6ca..95fed038b 100644 --- a/src/util/type-utils.ts +++ b/src/util/type-utils.ts @@ -44,17 +44,11 @@ export type ValueType = T[keyof T] * // Row == Person & Movie * ``` */ -export type RowType = UnionToIntersection - -/** - * Evil typescript magic to convert a union type `A | B | C` into an - * intersection type `A & B & C`. - */ -type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( - k: infer I -) => void - ? I - : never +export type RowType = { + [C in AnyColumn]: { + [T in TB]: C extends keyof DB[T] ? DB[T][C] : never + }[TB] +} /** * Given a database type and a union of table names in that db, returns @@ -92,6 +86,13 @@ export type AnyColumn = { }[TB] & string +/** + * Extracts a column type. + */ +export type ExtractColumnType = { + [T in TB]: C extends keyof DB[T] ? DB[T][C] : never +}[TB] + /** * Given a database type and a union of table names in that db, returns * a union type with all possible `table`.`column` combinations. diff --git a/test/src/test-setup.ts b/test/src/test-setup.ts index 7f4823d35..0b5082d14 100644 --- a/test/src/test-setup.ts +++ b/test/src/test-setup.ts @@ -312,9 +312,9 @@ async function insertToysForPet( .executeTakeFirst() } -async function insert( +async function insert( dialect: BuiltInDialect, - qb: InsertQueryBuilder + qb: InsertQueryBuilder ): Promise { if (dialect === 'postgres') { const { id } = await qb.returning('id').executeTakeFirstOrThrow() diff --git a/test/typings/index.test-d.ts b/test/typings/index.test-d.ts index 3e75c17e0..c2585dd89 100644 --- a/test/typings/index.test-d.ts +++ b/test/typings/index.test-d.ts @@ -396,7 +396,16 @@ async function testJoin(db: Kysely) { .selectAll() .execute() - expectType<(Person & Movie)[]>(r1) + expectType< + { + id: number | string + first_name: string + last_name: string | null + age: number + gender: 'male' | 'female' | 'other' + stars: number + }[] + >(r1) const r2 = await db .selectFrom('person') @@ -443,7 +452,17 @@ async function testJoin(db: Kysely) { .selectAll() .executeTakeFirstOrThrow() - expectType & Nullable>(r5) + expectType<{ + id: number | string | null + first_name: string + last_name: string | null + age: number + gender: 'male' | 'female' | 'other' + name: string | null + species: 'dog' | 'cat' | null + owner_id: number | null + stars: number | null + }>(r5) const r6 = await db .selectFrom('person') @@ -452,7 +471,17 @@ async function testJoin(db: Kysely) { .selectAll() .executeTakeFirstOrThrow() - expectType & Nullable & Movie>(r6) + expectType<{ + id: number | string | null + first_name: string | null + last_name: string | null + age: number | null + gender: 'male' | 'female' | 'other' | null + name: string | null + species: 'dog' | 'cat' | null + owner_id: number | null + stars: number + }>(r6) const r7 = await db .selectFrom('person') @@ -461,7 +490,17 @@ async function testJoin(db: Kysely) { .selectAll() .executeTakeFirstOrThrow() - expectType & Nullable & Nullable>(r7) + expectType<{ + id: number | string | null + first_name: string | null + last_name: string | null + age: number | null + gender: 'male' | 'female' | 'other' | null + name: string | null + species: 'dog' | 'cat' | null + owner_id: number | null + stars: number | null + }>(r7) // Refer to table that's not joined expectError(