3.4.8: TransactionSql lost call signatures due to Omit
## 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 条评论