ITADN

3.4.8: TransactionSql lost call signatures due to Omit

#1156Closeddanieltroger 创建于 2026-02-12
D
danieltrogercommented
## Description In v3.4.8, commit 36a53f6 changed `TransactionSql` from: ```typescript interface TransactionSql<TTypes extends Record<string, unknown> = {}> extends Sql<TTypes> { ``` to: ```typescript interface TransactionSql<TTypes extends Record<string, unknown> = {}> extends Omit<Sql<TTypes>, 'parameters' | 'largeObject' | 'subscribe' | 'CLOSE' | 'END' | 'PostgresError' | 'options' | 'reserve' | 'listen' | 'begin' | 'close' | 'end' > { ``` The intent is correct (preventing use of `tx.begin()`, `tx.listen()` etc. which don't work inside transactions), but `Omit` in TypeScript creates a mapped type that only preserves named properties. It silently drops call signatures, so `TransactionSql` is no longer callable as a tagged template literal. ## Reproduction ```typescript import postgres from "postgres"; const sql = postgres("..."); // Works fine await sql`SELECT 1`; // TS error: "This expression is not callable. Type 'TransactionSql<{}>' has no call signatures." await sql.begin(async (tx) => { await tx`SELECT 1`; }); ``` ## Suggested fix Replace `Omit` with a hand-written interface that explicitly re-declares the two call signatures from `Sql`: ```typescript interface TransactionSql<TTypes extends Record<string, unknown> = {}> { // Re-declare call signatures (Omit strips these) <T, K extends Rest<T>>(first: T & First<T, K, TTypes[keyof TTypes]>, ...rest: K): Return<T, K>; <T extends readonly (object | undefined)[] = Row[]>( template: TemplateStringsArray, ...parameters: readonly (ParameterOrFragment<TTypes[keyof TTypes]>)[] ): PendingQuery<T>; // Pick only the methods that are valid inside a transaction unsafe: Sql<TTypes>['unsafe']; typed: Sql<TTypes>['typed']; types: Sql<TTypes>['types']; notify: Sql<TTypes>['notify']; array: Sql<TTypes>['array']; file: Sql<TTypes>['file']; json: Sql<TTypes>['json']; savepoint<T>(cb: (sql: TransactionSql<TTypes>) => T | Promise<T>): Promise<UnwrapPromiseArray<T>>; savepoint<T>(name: string, cb: (sql: TransactionSql<TTypes>) => T | Promise<T>): Promise<UnwrapPromiseArray<T>>; prepare<T>(name: string): Promise<UnwrapPromiseArray<T>>; } ``` This achieves the same goal (no `begin`, `listen`, `subscribe`, etc. on `tx`) without losing the call signatures.
关闭于 2026-02-12 2 条评论