1
0
mirror of https://github.com/redis/node-redis.git synced 2025-12-09 21:21:11 +03:00

fix: add typed/untyped mode support for multi-commands (#3084)

This commit is contained in:
Pavel Pashov
2025-10-01 16:33:22 +03:00
committed by Nikolay Karadzhov
parent 7b56e9a3dc
commit 17fdb8c667
4 changed files with 30 additions and 8 deletions

View File

@@ -10,7 +10,7 @@ import { TcpSocketConnectOpts } from 'node:net';
import { PUBSUB_TYPE, PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub'; import { PUBSUB_TYPE, PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub';
import { Command, CommandSignature, TypeMapping, CommanderConfig, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions, RedisArgument, ReplyWithTypeMapping, SimpleStringReply, TransformReply, CommandArguments } from '../RESP/types'; import { Command, CommandSignature, TypeMapping, CommanderConfig, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions, RedisArgument, ReplyWithTypeMapping, SimpleStringReply, TransformReply, CommandArguments } from '../RESP/types';
import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command';
import { RedisMultiQueuedCommand } from '../multi-command'; import { MULTI_MODE, MultiMode, RedisMultiQueuedCommand } from '../multi-command';
import HELLO, { HelloOptions } from '../commands/HELLO'; import HELLO, { HelloOptions } from '../commands/HELLO';
import { ScanOptions, ScanCommonOptions } from '../commands/SCAN'; import { ScanOptions, ScanCommonOptions } from '../commands/SCAN';
import { RedisLegacyClient, RedisLegacyClientType } from './legacy-mode'; import { RedisLegacyClient, RedisLegacyClientType } from './legacy-mode';
@@ -1187,8 +1187,8 @@ export default class RedisClient<
return execResult as Array<unknown>; return execResult as Array<unknown>;
} }
MULTI() { MULTI<isTyped extends MultiMode = MULTI_MODE['TYPED']>() {
type Multi = new (...args: ConstructorParameters<typeof RedisClientMultiCommand>) => RedisClientMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING>; type Multi = new (...args: ConstructorParameters<typeof RedisClientMultiCommand>) => RedisClientMultiCommandType<isTyped, [], M, F, S, RESP, TYPE_MAPPING>;
return new ((this as any).Multi as Multi)( return new ((this as any).Multi as Multi)(
this._executeMulti.bind(this), this._executeMulti.bind(this),
this._executePipeline.bind(this), this._executePipeline.bind(this),

View File

@@ -1,5 +1,5 @@
import COMMANDS from '../commands'; import COMMANDS from '../commands';
import RedisMultiCommand, { MULTI_REPLY, MultiReply, MultiReplyType, RedisMultiQueuedCommand } from '../multi-command'; import RedisMultiCommand, { MULTI_MODE, MULTI_REPLY, MultiMode, MultiReply, MultiReplyType, RedisMultiQueuedCommand } from '../multi-command';
import { ReplyWithTypeMapping, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, RedisScript, RedisFunction, TypeMapping } from '../RESP/types'; import { ReplyWithTypeMapping, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, RedisScript, RedisFunction, TypeMapping } from '../RESP/types';
import { attachConfig, functionArgumentsPrefix, getTransformReply } from '../commander'; import { attachConfig, functionArgumentsPrefix, getTransformReply } from '../commander';
import { BasicCommandParser } from './parser'; import { BasicCommandParser } from './parser';
@@ -13,7 +13,7 @@ type CommandSignature<
S extends RedisScripts, S extends RedisScripts,
RESP extends RespVersions, RESP extends RespVersions,
TYPE_MAPPING extends TypeMapping TYPE_MAPPING extends TypeMapping
> = (...args: Tail<Parameters<C['parseCommand']>>) => RedisClientMultiCommandType< > = (...args: Tail<Parameters<C['parseCommand']>>) => InternalRedisClientMultiCommandType<
[...REPLIES, ReplyWithTypeMapping<CommandReply<C, RESP>, TYPE_MAPPING>], [...REPLIES, ReplyWithTypeMapping<CommandReply<C, RESP>, TYPE_MAPPING>],
M, M,
F, F,
@@ -70,7 +70,7 @@ type WithScripts<
[P in keyof S]: CommandSignature<REPLIES, S[P], M, F, S, RESP, TYPE_MAPPING>; [P in keyof S]: CommandSignature<REPLIES, S[P], M, F, S, RESP, TYPE_MAPPING>;
}; };
export type RedisClientMultiCommandType< type InternalRedisClientMultiCommandType<
REPLIES extends Array<any>, REPLIES extends Array<any>,
M extends RedisModules, M extends RedisModules,
F extends RedisFunctions, F extends RedisFunctions,
@@ -85,6 +85,19 @@ export type RedisClientMultiCommandType<
WithScripts<REPLIES, M, F, S, RESP, TYPE_MAPPING> WithScripts<REPLIES, M, F, S, RESP, TYPE_MAPPING>
); );
type TypedOrAny<Flag extends MultiMode, T> =
[Flag] extends [MULTI_MODE['TYPED']] ? T : any;
export type RedisClientMultiCommandType<
isTyped extends MultiMode,
REPLIES extends Array<any>,
M extends RedisModules,
F extends RedisFunctions,
S extends RedisScripts,
RESP extends RespVersions,
TYPE_MAPPING extends TypeMapping
> = TypedOrAny<isTyped, InternalRedisClientMultiCommandType<REPLIES, M, F, S, RESP, TYPE_MAPPING>>;
type ExecuteMulti = (commands: Array<RedisMultiQueuedCommand>, selectedDB?: number) => Promise<Array<unknown>>; type ExecuteMulti = (commands: Array<RedisMultiQueuedCommand>, selectedDB?: number) => Promise<Array<unknown>>;
export default class RedisClientMultiCommand<REPLIES = []> { export default class RedisClientMultiCommand<REPLIES = []> {

View File

@@ -10,6 +10,7 @@ import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-co
import { BasicPooledClientSideCache, ClientSideCacheConfig, PooledClientSideCacheProvider } from './cache'; import { BasicPooledClientSideCache, ClientSideCacheConfig, PooledClientSideCacheProvider } from './cache';
import { BasicCommandParser } from './parser'; import { BasicCommandParser } from './parser';
import SingleEntryCache from '../single-entry-cache'; import SingleEntryCache from '../single-entry-cache';
import { MULTI_MODE, MultiMode } from '../multi-command';
export interface RedisPoolOptions { export interface RedisPoolOptions {
/** /**
@@ -486,8 +487,9 @@ export class RedisClientPool<
return this.execute(client => client.sendCommand(args, options)); return this.execute(client => client.sendCommand(args, options));
} }
MULTI() {
type Multi = new (...args: ConstructorParameters<typeof RedisClientMultiCommand>) => RedisClientMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING>; MULTI<isTyped extends MultiMode = MULTI_MODE['TYPED']>() {
type Multi = new (...args: ConstructorParameters<typeof RedisClientMultiCommand>) => RedisClientMultiCommandType<isTyped, [], M, F, S, RESP, TYPE_MAPPING>;
return new ((this as any).Multi as Multi)( return new ((this as any).Multi as Multi)(
(commands, selectedDB) => this.execute(client => client._executeMulti(commands, selectedDB)), (commands, selectedDB) => this.execute(client => client._executeMulti(commands, selectedDB)),
commands => this.execute(client => client._executePipeline(commands)), commands => this.execute(client => client._executePipeline(commands)),

View File

@@ -6,6 +6,13 @@ export type MULTI_REPLY = {
TYPED: 'typed'; TYPED: 'typed';
}; };
export type MULTI_MODE = {
TYPED: 'typed';
UNTYPED: 'untyped';
};
export type MultiMode = MULTI_MODE[keyof MULTI_MODE];
export type MultiReply = MULTI_REPLY[keyof MULTI_REPLY]; export type MultiReply = MULTI_REPLY[keyof MULTI_REPLY];
export type MultiReplyType<T extends MultiReply, REPLIES> = T extends MULTI_REPLY['TYPED'] ? REPLIES : Array<ReplyUnion>; export type MultiReplyType<T extends MultiReply, REPLIES> = T extends MULTI_REPLY['TYPED'] ? REPLIES : Array<ReplyUnion>;