使用Kysely编写通用Repository时的TypeScript类型报错问题
Kysely通用Repository泛型类型错误解决:
string类型不匹配OperandValueExpressionOrList 问题重现
在编写Kysely通用Repository时,findOne方法的where("uuid", "=", id)语句触发类型错误:
Argument of type 'string' is not assignable to parameter of type 'OperandValueExpressionOrList<Tables, ExtractTableAlias<Tables, TableName>, "uuid">'.(2345)
相关代码示例:
import { Kysely, Selectable } from "kysely"; export type FooTable = { uuid: string; name: string; fromFoo: boolean; }; export type BarTable = { uuid: string; name: string; fromBar: string; } type Tables = { foo: FooTable; bar: BarTable; }; type Data = { uuid: string; name: string; [key: string]: unknown; } export interface GenericRepository<T extends Data> { findOne(id: Data["uuid"]): Promise<T | null>; } export function createGenericRepository< D extends Data, TableName extends keyof Tables, T extends Tables[TableName] >({ dataSource, tableName, toModel, }: { dataSource: Kysely<Tables>; tableName: TableName; toModel: (data: Selectable<T>) => D; }): GenericRepository<D> { return { async findOne(id) { const data = await dataSource .selectFrom(tableName) .selectAll() .where("uuid", "=", id) // 此处报错 .executeTakeFirst(); if (!data) { return null; } return toModel(data); } }; } function createFooRepository(dataSource: Kysely<Tables>) { return createGenericRepository({ dataSource, tableName: "foo", toModel: (data) => { return { uuid: "123", name: "name", fromFoo: true } } }) }
错误原因
Kysely的where方法对参数类型有严格的推导逻辑,当前泛型定义存在两个问题:
- 冗余的泛型参数
T extends Tables[TableName]导致TypeScript无法精准关联表字段类型与传入的id类型; GenericRepository中findOne的id类型固定为Data["uuid"],未与具体表的uuid字段类型绑定,导致类型不匹配。
解决方案
方案1:简化泛型+类型断言
调整泛型参数结构,移除冗余定义,并通过类型断言让TypeScript明确id与表uuid字段的类型匹配:
import { Kysely, Selectable } from "kysely"; export type FooTable = { uuid: string; name: string; fromFoo: boolean; }; export type BarTable = { uuid: string; name: string; fromBar: string; } type Tables = { foo: FooTable; bar: BarTable; }; type Data = { uuid: string; name: string; [key: string]: unknown; } export interface GenericRepository<T extends Data> { findOne(id: T["uuid"]): Promise<T | null>; } export function createGenericRepository< TableName extends keyof Tables, D extends Data >({ dataSource, tableName, toModel, }: { dataSource: Kysely<Tables>; tableName: TableName; toModel: (data: Selectable<Tables[TableName]>) => D; }): GenericRepository<D> { return { async findOne(id) { const data = await dataSource .selectFrom(tableName) .selectAll() .where("uuid", "=", id as Tables[TableName]["uuid"]) .executeTakeFirst(); if (!data) { return null; } return toModel(data); } }; } function createFooRepository(dataSource: Kysely<Tables>) { return createGenericRepository({ dataSource, tableName: "foo", toModel: (data) => ({ uuid: data.uuid, name: data.name, fromFoo: data.fromFoo }) }) }
方案2:严格泛型约束(无需断言)
通过泛型约束直接绑定表类型与模型的uuid类型,完全满足Kysely的类型要求:
import { Kysely, Selectable } from "kysely"; export type FooTable = { uuid: string; name: string; fromFoo: boolean; }; export type BarTable = { uuid: string; name: string; fromBar: string; } type Tables = { foo: FooTable; bar: BarTable; }; type Data = { uuid: string; name: string; [key: string]: unknown; } export interface GenericRepository<T extends Data> { findOne(id: T["uuid"]): Promise<T | null>; } export function createGenericRepository< TableName extends keyof Tables, TableType extends Tables[TableName], D extends Data & { uuid: TableType["uuid"] } >({ dataSource, tableName, toModel, }: { dataSource: Kysely<Tables>; tableName: TableName; toModel: (data: Selectable<TableType>) => D; }): GenericRepository<D> { return { async findOne(id) { const data = await dataSource .selectFrom(tableName) .selectAll() .where("uuid", "=", id) .executeTakeFirst(); if (!data) { return null; } return toModel(data); } }; } function createFooRepository(dataSource: Kysely<Tables>) { return createGenericRepository({ dataSource, tableName: "foo", toModel: (data) => ({ uuid: data.uuid, name: data.name, fromFoo: data.fromFoo }) }) }
关键调整点
- 移除冗余泛型参数,减少类型推导复杂度;
- 让
findOne的id类型与模型/表的uuid字段类型绑定; - 修正
toModel实现,避免硬编码返回值,保证类型一致性。
内容的提问来源于stack exchange,提问作者Guid
相关产品推荐
相关产品推荐

