Skip to content

Commit 1e10c95

Browse files
committedMar 14, 2022
feat: add declarations for methods
1 parent 4e8c567 commit 1e10c95

15 files changed

+12661
-31
lines changed
 

‎.eslintrc.json

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"plugin:@typescript-eslint/eslint-recommended",
66
"prettier"
77
],
8+
"ignorePatterns": ["bin/generateRedisCommander/template.ts"],
89
"parser": "@typescript-eslint/parser",
910
"plugins": ["@typescript-eslint"],
1011
"env": { "node": true },
@@ -18,6 +19,7 @@
1819
"@typescript-eslint/no-this-alias": 0,
1920
"@typescript-eslint/ban-ts-ignore": 0,
2021
"@typescript-eslint/ban-ts-comment": 0,
22+
"@typescript-eslint/adjacent-overload-signatures": 0,
2123
"@typescript-eslint/ban-types": 0,
2224
"@typescript-eslint/no-empty-interface": 0,
2325
"@typescript-eslint/no-empty-function": 0,

‎.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
node_modules
22
*.cpuprofile
3-
/test.js
3+
/test.*
44
/.idea
55
built
66

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const typeMaps = require("./typeMaps");
2+
3+
module.exports = {
4+
debug: [
5+
[{ name: "subcommand", type: "string" }],
6+
[
7+
{ name: "subcommand", type: "string" },
8+
{ name: "args", type: typeMaps.string, multiple: true },
9+
],
10+
],
11+
};

‎bin/generateRedisCommander/index.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const returnTypes = require("./returnTypes");
2+
const argumentTypes = require("./argumentTypes");
3+
const typeMaps = require("./typeMaps");
4+
const { getCommanderInterface } = require("ioredis-interface-generator");
5+
6+
const ignoredCommands = ["monitor", "multi"];
7+
const commands = require("redis-commands").list.filter(
8+
(name) => !ignoredCommands.includes(name)
9+
);
10+
11+
const fs = require("fs");
12+
const path = require("path");
13+
14+
const template = fs.readFileSync(path.join(__dirname, "/template.ts"), "utf8");
15+
16+
async function main() {
17+
const interface = await getCommanderInterface({
18+
commands,
19+
complexityLimit: 50,
20+
redisOpts: {
21+
port: process.env.REDIS_PORT,
22+
},
23+
returnTypes,
24+
argumentTypes,
25+
typeMaps: typeMaps,
26+
});
27+
28+
fs.writeFileSync(
29+
path.join(__dirname, "..", "..", "lib/utils/RedisCommander.ts"),
30+
template.replace("////", () => interface)
31+
);
32+
}
33+
34+
main()
35+
.catch(console.error)
36+
.then(() => process.exit(0));
+222
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
module.exports = {
2+
multi: "ChainableCommander",
3+
get: "string | null",
4+
set: (types) => {
5+
if (types.find((type) => type.includes("GET"))) {
6+
return "string | null";
7+
}
8+
if (types.find((type) => type.includes("NX") || type.includes("XX"))) {
9+
return "'OK' | null";
10+
}
11+
return "'OK'";
12+
},
13+
ping: (types) => {
14+
return types.length ? "string" : "'PONG'";
15+
},
16+
append: "number",
17+
asking: "'OK'",
18+
auth: "'OK'",
19+
bgrewriteaof: "string",
20+
bgsave: "'OK'",
21+
bitcount: "number",
22+
bitfield_ro: "unknown[]",
23+
bitop: "number",
24+
bitpos: "number",
25+
blpop: "[string, string] | null",
26+
brpop: "[string, string] | null",
27+
brpoplpush: "string | null",
28+
blmove: "string | null",
29+
lmpop: "unknown[] | null",
30+
blmpop: "unknown[] | null",
31+
bzpopmin: "unknown[] | null",
32+
bzpopmax: "unknown[] | null",
33+
command: "unknown[]",
34+
copy: "number",
35+
dbsize: "number",
36+
decr: "number",
37+
decrby: "number",
38+
del: "number",
39+
discard: "'OK'",
40+
dump: "string",
41+
echo: "string",
42+
exec: "[error: Error, result: unknown][] | null",
43+
exists: "number",
44+
expire: "number",
45+
expireat: "number",
46+
expiretime: "number",
47+
failover: "'OK'",
48+
flushall: "'OK'",
49+
flushdb: "'OK'",
50+
geoadd: "number",
51+
geohash: "string[]",
52+
geopos: "unknown[] | null",
53+
geodist: "string | null",
54+
georadius: "unknown[]",
55+
geosearch: "unknown[]",
56+
geosearchstore: "number",
57+
getbit: "number",
58+
getdel: "string | null",
59+
getex: "string | null",
60+
getrange: "string",
61+
getset: "string | null",
62+
hdel: "number",
63+
hello: "unknown[]",
64+
hexists: "number",
65+
hget: "string | null",
66+
hincrby: "number",
67+
hincrbyfloat: "string",
68+
hkeys: "string[]",
69+
hlen: "number",
70+
hmget: "(string | null)[]",
71+
hmset: "'OK'",
72+
hset: "number",
73+
hsetnx: "number",
74+
hrandfield: "string | unknown[] | null",
75+
hstrlen: "number",
76+
hvals: "string[]",
77+
incr: "number",
78+
incrby: "number",
79+
incrbyfloat: "string",
80+
info: "string",
81+
lolwut: "string",
82+
keys: "string[]",
83+
lastsave: "number",
84+
lindex: "string | null",
85+
linsert: "number",
86+
llen: "number",
87+
lpop: "string" | "unknown[] | null",
88+
lpos: null,
89+
lpush: "number",
90+
lpushx: "number",
91+
lrange: "string[]",
92+
lrem: "number",
93+
lset: "'OK'",
94+
ltrim: "'OK'",
95+
mget: "(string | null)[]",
96+
migrate: "'OK'",
97+
move: "number",
98+
mset: "'OK'",
99+
msetnx: "number",
100+
persist: "number",
101+
pexpire: "number",
102+
pexpireat: "number",
103+
pexpiretime: "number",
104+
pfadd: "number",
105+
pfcount: "number",
106+
pfmerge: "'OK'",
107+
pubsub: "unknown[]",
108+
pttl: "number",
109+
publish: "number",
110+
quit: "'OK'",
111+
randomkey: "string | null",
112+
readonly: "'OK'",
113+
readwrite: "'OK'",
114+
rename: "'OK'",
115+
renamenx: "number",
116+
reset: "'OK'",
117+
restore: "'OK'",
118+
role: "unknown[]",
119+
rpop: "string" | "unknown[] | null",
120+
rpoplpush: "string",
121+
lmove: "string",
122+
rpush: "number",
123+
rpushx: "number",
124+
sadd: "number",
125+
save: "'OK'",
126+
scard: "number",
127+
sdiff: "string[]",
128+
sdiffstore: "number",
129+
select: "'OK'",
130+
setbit: "number",
131+
setex: "'OK'",
132+
setnx: "number",
133+
setrange: "number",
134+
shutdown: "'OK'",
135+
sinter: "string[]",
136+
sintercard: "number",
137+
sinterstore: "number",
138+
sismember: "number",
139+
smismember: "unknown[]",
140+
slaveof: "'OK'",
141+
replicaof: "'OK'",
142+
smembers: "string[]",
143+
smove: "number",
144+
sort: "number" | "unknown[]",
145+
sortRo: "unknown[]",
146+
spop: (types) => (types.length > 1 ? "string[]" : "string | null"),
147+
srandmember: "string" | "unknown[] | null",
148+
srem: "number",
149+
strlen: "number",
150+
sunion: "string[]",
151+
sunionstore: "number",
152+
swapdb: "'OK'",
153+
time: "number[]",
154+
touch: "number",
155+
ttl: "number",
156+
type: "string",
157+
unlink: "number",
158+
unwatch: "'OK'",
159+
wait: "number",
160+
watch: "'OK'",
161+
zadd: (types) => {
162+
if (types.find((type) => type.includes("INCR"))) {
163+
if (types.find((type) => type.includes("XX") || type.includes("NX"))) {
164+
return "string | null";
165+
}
166+
return "string";
167+
}
168+
return "number";
169+
},
170+
zcard: "number",
171+
zcount: "number",
172+
zdiff: "string[]",
173+
zdiffstore: "number",
174+
zincrby: "string",
175+
zinter: "string[]",
176+
zintercard: "number",
177+
zinterstore: "number",
178+
zlexcount: "number",
179+
zpopmax: "string[]",
180+
zpopmin: "string[]",
181+
zrandmember: (types) => {
182+
return types.includes("number") ? "string | null" : "string[]";
183+
},
184+
zrangestore: "number",
185+
zrange: "string[]",
186+
zrangebylex: "string[]",
187+
zrevrangebylex: "string[]",
188+
zrangebyscore: "string[]",
189+
zrank: "number | null",
190+
zrem: "number",
191+
zremrangebylex: "number",
192+
zremrangebyrank: "number",
193+
zremrangebyscore: "number",
194+
zrevrange: "string[]",
195+
zrevrangebyscore: "string[]",
196+
zrevrank: "number | null",
197+
zscore: "string",
198+
zunion: "unknown[]",
199+
zmscore: "unknown[] | null",
200+
zunionstore: "number",
201+
scan: "[cursor: string, elements: string[]]",
202+
sscan: "[cursor: string, elements: string[]]",
203+
hscan: "[cursor: string, elements: string[]]",
204+
zscan: "[cursor: string, elements: string[]]",
205+
xadd: "string | null",
206+
xtrim: "number",
207+
xdel: "number",
208+
xrange: "[id: string, fields: string[]][]",
209+
xrevrange: "[id: string, fields: string[]][]",
210+
xlen: "number",
211+
xread: (types) => {
212+
if (types.find((type) => type.includes("BLOCK"))) {
213+
return "unknown[] | null";
214+
}
215+
return "unknown[]";
216+
},
217+
xreadgroup: "unknown[]",
218+
xack: "number",
219+
xclaim: "unknown[]",
220+
xautoclaim: "unknown[]",
221+
xpending: "unknown[]",
222+
};
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
type RedisKey = string | Buffer;
2+
type RedisValue = string | Buffer | number;
3+
type Callback<T> = (err: Error | null, res: T) => void;
4+
5+
// Inspired by https://github.com/mmkal/handy-redis/blob/main/src/generated/interface.ts.
6+
// Should be fixed with https://github.com/Microsoft/TypeScript/issues/1213
7+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
8+
export interface ResultTypes<Result, Context> {
9+
default: Promise<Result>;
10+
pipeline: ChainableCommander;
11+
}
12+
13+
export interface ChainableCommander
14+
extends RedisCommander<{ type: "pipeline" }> {}
15+
16+
export type ClientContext = { type: keyof ResultTypes<unknown, unknown> };
17+
export type Result<T, Context extends ClientContext> =
18+
// prettier-break
19+
ResultTypes<T, Context>[Context["type"]];
20+
21+
interface RedisCommander<Context extends ClientContext = { type: "default" }> {
22+
multi(): ChainableCommander;
23+
////
24+
}
25+
26+
export default RedisCommander;
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
key: "RedisKey",
3+
string: "string | Buffer | number",
4+
pattern: "string",
5+
number: "number | string",
6+
};

‎lib/Redis.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ import { EventEmitter } from "events";
22
import * as commands from "redis-commands";
33
import asCallback from "standard-as-callback";
44
import { AbstractConnector, Command, ScanStream, SentinelConnector } from ".";
5-
import Commander from "./utils/Commander";
65
import { StandaloneConnector } from "./connectors";
76
import * as eventHandler from "./redis/event_handler";
87
import {
98
DEFAULT_REDIS_OPTIONS,
109
ReconnectOnError,
1110
RedisOptions,
1211
} from "./redis/RedisOptions";
13-
import { addTransactionSupport } from "./transaction";
12+
import { addTransactionSupport, Transaction } from "./transaction";
1413
import { CallbackFunction, ICommandItem, NetStream } from "./types";
1514
import {
1615
CONNECTION_CLOSED_ERROR_MSG,
@@ -20,6 +19,7 @@ import {
2019
resolveTLSProfile,
2120
} from "./utils";
2221
import applyMixin from "./utils/applyMixin";
22+
import Commander from "./utils/Commander";
2323
import { defaults, noop } from "./utils/lodash";
2424
import Deque = require("denque");
2525
const debug = Debug("redis");
@@ -409,7 +409,6 @@ class Redis extends Commander {
409409
this.condition.select !== item.select &&
410410
item.command.name !== "select"
411411
) {
412-
// @ts-expect-error
413412
this.select(item.select);
414413
}
415414
// TODO
@@ -460,8 +459,7 @@ class Redis extends Commander {
460459
*/
461460
private _readyCheck(callback: CallbackFunction) {
462461
const _this = this;
463-
// @ts-expect-error
464-
this.info(function (err: Error | null, res: string) {
462+
this.info(function (err, res) {
465463
if (err) {
466464
return callback(err);
467465
}
@@ -782,5 +780,6 @@ applyMixin(Redis, EventEmitter);
782780
});
783781

784782
addTransactionSupport(Redis.prototype);
783+
interface Redis extends Transaction {}
785784

786785
export default Redis;

‎lib/autoPipelining.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export function shouldUseAutoPipelining(
9494
*/
9595
export function getFirstValueInFlattenedArray(
9696
args: ArgumentType[]
97-
): string | undefined {
97+
): string | Buffer | number | null | undefined {
9898
for (let i = 0; i < args.length; i++) {
9999
const arg = args[i];
100100
if (typeof arg === "string") {
@@ -107,7 +107,7 @@ export function getFirstValueInFlattenedArray(
107107
}
108108
const flattened = flatten([arg]);
109109
if (flattened.length > 0) {
110-
return String(flattened[0]);
110+
return flattened[0];
111111
}
112112
}
113113
return undefined;

‎lib/pipeline.ts

+1-9
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function generateMultiWithNodes(redis, keys) {
2727
return slot;
2828
}
2929

30-
class Pipeline extends Commander {
30+
class Pipeline extends Commander<{ type: "pipeline" }> {
3131
isCluster: boolean;
3232
isPipeline = true;
3333
leftRedirections: { ttl?: number };
@@ -155,7 +155,6 @@ class Pipeline extends Commander {
155155
this.leftRedirections = {};
156156
}
157157
const exec = function () {
158-
// @ts-expect-error
159158
_this.exec();
160159
};
161160
const cluster = this.redis as Cluster;
@@ -166,12 +165,10 @@ class Pipeline extends Commander {
166165
cluster._groupsBySlot[errv[1]] =
167166
cluster._groupsIds[cluster.slots[errv[1]].join(";")];
168167
cluster.refreshSlotsCache();
169-
// @ts-expect-error
170168
_this.exec();
171169
},
172170
ask: function (_slot: string, key: string) {
173171
_this.preferKey = key;
174-
// @ts-expect-error
175172
_this.exec();
176173
},
177174
tryagain: exec,
@@ -236,19 +233,14 @@ class Pipeline extends Commander {
236233

237234
export default Pipeline;
238235

239-
// @ts-expect-error
240236
const multi = Pipeline.prototype.multi;
241-
// @ts-expect-error
242237
Pipeline.prototype.multi = function () {
243238
this._transactions += 1;
244239
return multi.apply(this, arguments);
245240
};
246241

247-
// @ts-expect-error
248242
const execBuffer = Pipeline.prototype.execBuffer;
249-
// @ts-expect-error
250243
const exec = Pipeline.prototype.exec;
251-
// @ts-expect-error
252244
Pipeline.prototype.execBuffer = deprecate(function () {
253245
if (this._transactions > 0) {
254246
this._transactions -= 1;

‎lib/transaction.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ import { wrapMultiResult, noop } from "./utils";
22
import asCallback from "standard-as-callback";
33
import Pipeline from "./pipeline";
44
import { CallbackFunction } from "./types";
5+
import { ChainableCommander } from "./utils/RedisCommander";
6+
7+
export interface Transaction {
8+
pipeline(): ChainableCommander;
9+
multi(): ChainableCommander;
10+
}
511

612
export function addTransactionSupport(redis) {
713
redis.pipeline = function (commands) {
@@ -22,14 +28,11 @@ export function addTransactionSupport(redis) {
2228
return multi.call(this);
2329
}
2430
const pipeline = new Pipeline(this);
25-
// @ts-expect-error
2631
pipeline.multi();
2732
if (Array.isArray(commands)) {
2833
pipeline.addBatch(commands);
2934
}
30-
// @ts-expect-error
3135
const exec = pipeline.exec;
32-
// @ts-expect-error
3336
pipeline.exec = function (callback: CallbackFunction) {
3437
// Wait for the cluster to be connected, since we need nodes information before continuing
3538
if (this.isCluster && !this.redis.slots.length) {
@@ -82,14 +85,11 @@ export function addTransactionSupport(redis) {
8285
);
8386
};
8487

85-
// @ts-expect-error
8688
const { execBuffer } = pipeline;
87-
// @ts-expect-error
8889
pipeline.execBuffer = function (callback: CallbackFunction) {
8990
if (this._transactions > 0) {
9091
execBuffer.call(pipeline);
9192
}
92-
// @ts-expect-error
9393
return pipeline.exec(callback);
9494
};
9595
return pipeline;

‎lib/utils/Commander.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
import Command, { ArgumentType } from "../command";
77
import Script from "../script";
88
import { CallbackFunction, NetStream } from "../types";
9+
import RedisCommander, { ClientContext } from "./RedisCommander";
910

1011
export interface CommanderOptions {
1112
keyPrefix?: string;
@@ -26,7 +27,9 @@ const DROP_BUFFER_SUPPORT_ERROR =
2627
* Will decrease the performance significantly.
2728
* @constructor
2829
*/
29-
class Commander {
30+
31+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
32+
class Commander<Context extends ClientContext = { type: "default" }> {
3033
options: CommanderOptions = {};
3134
scriptsSet = {};
3235
addedBuiltinSet = new Set<string>();
@@ -95,6 +98,8 @@ class Commander {
9598
}
9699
}
97100

101+
interface Commander<Context> extends RedisCommander<Context> {}
102+
98103
const commands = require("redis-commands").list.filter(function (command) {
99104
return command !== "monitor";
100105
});

‎lib/utils/RedisCommander.ts

+12,336
Large diffs are not rendered by default.

‎test/unit/autoPipelining.ts

-5
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,10 @@ describe("autoPipelining", function () {
1616
expectGetFirstValueIs(["key", "value"], "key");
1717
expectGetFirstValueIs([[], "key"], "key");
1818
expectGetFirstValueIs([["key"]], "key");
19-
// @ts-expect-error
2019
expectGetFirstValueIs([[["key"]]], ["key"]);
21-
// @ts-expect-error
2220
expectGetFirstValueIs([0, 1, 2, 3, 4], 0);
23-
// @ts-expect-error
2421
expectGetFirstValueIs([[true]], true);
25-
// @ts-expect-error
2622
expectGetFirstValueIs([Buffer.from("test")], Buffer.from("test"));
27-
// @ts-expect-error
2823
expectGetFirstValueIs([{}], {});
2924
// lodash.isArguments is true for this legacy js way to get argument lists
3025
const createArguments = function () {

‎test/unit/utils.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -306,9 +306,9 @@ describe("utils", function () {
306306
tls: { profile: "RedisCloudFixed", key: "foo" },
307307
};
308308
const expected = {
309+
host: "localhost",
310+
port: 6379,
309311
tls: {
310-
host: "localhost",
311-
port: 6379,
312312
...TLSProfiles.RedisCloudFixed,
313313
key: "foo",
314314
},

0 commit comments

Comments
 (0)
Please sign in to comment.