Skip to content

Commit 99ced12

Browse files
committed
👍 Add isMap and isMapOf
1 parent 91cf555 commit 99ced12

File tree

4 files changed

+266
-79
lines changed

4 files changed

+266
-79
lines changed

__snapshots__/is_test.ts.snap

Lines changed: 86 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,40 @@
11
export const snapshot = {};
22

3-
snapshot[`isLiteralOf<T> > returns properly named function 1`] = `'isLiteralOf("hello")'`;
4-
5-
snapshot[`isLiteralOf<T> > returns properly named function 2`] = `"isLiteralOf(100)"`;
6-
7-
snapshot[`isLiteralOf<T> > returns properly named function 3`] = `"isLiteralOf(100n)"`;
3+
snapshot[`isInstanceOf<T> > returns properly named function 1`] = `"isInstanceOf(Date)"`;
84
9-
snapshot[`isLiteralOf<T> > returns properly named function 4`] = `"isLiteralOf(true)"`;
5+
snapshot[`isInstanceOf<T> > returns properly named function 2`] = `"isInstanceOf((anonymous))"`;
106
11-
snapshot[`isLiteralOf<T> > returns properly named function 5`] = `"isLiteralOf(null)"`;
7+
snapshot[`isMapOf<T> > returns properly named function 1`] = `"isMapOf(isNumber)"`;
128
13-
snapshot[`isLiteralOf<T> > returns properly named function 6`] = `"isLiteralOf(undefined)"`;
9+
snapshot[`isMapOf<T> > returns properly named function 2`] = `"isMapOf((anonymous))"`;
1410
15-
snapshot[`isLiteralOf<T> > returns properly named function 7`] = `"isLiteralOf(Symbol(asdf))"`;
11+
snapshot[`isTupleOf<T> > returns properly named function 1`] = `
12+
"isTupleOf([
13+
isNumber,
14+
isString,
15+
isBoolean
16+
])"
17+
`;
1618
17-
snapshot[`isRecordOf<T, K> > returns properly named function 1`] = `"isRecordOf(isNumber, isString)"`;
19+
snapshot[`isTupleOf<T> > returns properly named function 2`] = `"isTupleOf([(anonymous)])"`;
1820
19-
snapshot[`isRecordOf<T, K> > returns properly named function 2`] = `"isRecordOf((anonymous), isString)"`;
21+
snapshot[`isTupleOf<T> > returns properly named function 3`] = `
22+
"isTupleOf([
23+
isTupleOf([
24+
isTupleOf([
25+
isNumber,
26+
isString,
27+
isBoolean
28+
])
29+
])
30+
])"
31+
`;
2032
21-
snapshot[`isAllOf<T> > returns properly named function 1`] = `
22-
"isAllOf([
23-
isObjectOf({a: isNumber}),
24-
isObjectOf({b: isString})
33+
snapshot[`isOneOf<T> > returns properly named function 1`] = `
34+
"isOneOf([
35+
isNumber,
36+
isString,
37+
isBoolean
2538
])"
2639
`;
2740
@@ -47,108 +60,80 @@ snapshot[`isReadonlyTupleOf<T, E> > returns properly named function 3`] = `
4760
], isArray)"
4861
`;
4962
50-
snapshot[`isUniformTupleOf<T> > returns properly named function 1`] = `"isUniformTupleOf(3, isAny)"`;
63+
snapshot[`isOptionalOf<T> > returns properly named function 1`] = `"isOptionalOf(isNumber)"`;
5164
52-
snapshot[`isUniformTupleOf<T> > returns properly named function 2`] = `"isUniformTupleOf(3, isNumber)"`;
65+
snapshot[`isSetOf<T> > returns properly named function 1`] = `"isSetOf(isNumber)"`;
5366
54-
snapshot[`isUniformTupleOf<T> > returns properly named function 3`] = `"isUniformTupleOf(3, (anonymous))"`;
67+
snapshot[`isSetOf<T> > returns properly named function 2`] = `"isSetOf((anonymous))"`;
5568
56-
snapshot[`isTupleOf<T, E> > returns properly named function 1`] = `
57-
"isTupleOf([
69+
snapshot[`isReadonlyTupleOf<T> > returns properly named function 1`] = `
70+
"isReadonlyTupleOf([
5871
isNumber,
5972
isString,
6073
isBoolean
61-
], isArray)"
74+
])"
6275
`;
6376
64-
snapshot[`isTupleOf<T, E> > returns properly named function 2`] = `"isTupleOf([(anonymous)], isArrayOf(isString))"`;
77+
snapshot[`isReadonlyTupleOf<T> > returns properly named function 2`] = `"isReadonlyTupleOf([(anonymous)])"`;
6578
66-
snapshot[`isTupleOf<T, E> > returns properly named function 3`] = `
67-
"isTupleOf([
68-
isTupleOf([
69-
isTupleOf([
79+
snapshot[`isReadonlyTupleOf<T> > returns properly named function 3`] = `
80+
"isReadonlyTupleOf([
81+
isReadonlyTupleOf([
82+
isReadonlyTupleOf([
7083
isNumber,
7184
isString,
7285
isBoolean
73-
], isArray)
74-
], isArray)
86+
])
87+
])
7588
])"
7689
`;
7790
78-
snapshot[`isReadonlyUniformTupleOf<T> > returns properly named function 1`] = `"isReadonlyUniformTupleOf(3, isAny)"`;
91+
snapshot[`isRecordOf<T, K> > returns properly named function 1`] = `"isRecordOf(isNumber, isString)"`;
7992
80-
snapshot[`isReadonlyUniformTupleOf<T> > returns properly named function 2`] = `"isReadonlyUniformTupleOf(3, isNumber)"`;
93+
snapshot[`isRecordOf<T, K> > returns properly named function 2`] = `"isRecordOf((anonymous), isString)"`;
8194
82-
snapshot[`isReadonlyUniformTupleOf<T> > returns properly named function 3`] = `"isReadonlyUniformTupleOf(3, (anonymous))"`;
95+
snapshot[`isMapOf<T, K> > returns properly named function 1`] = `"isMapOf(isNumber, isString)"`;
96+
97+
snapshot[`isMapOf<T, K> > returns properly named function 2`] = `"isMapOf((anonymous), isString)"`;
8398
8499
snapshot[`isRecordOf<T> > returns properly named function 1`] = `"isRecordOf(isNumber)"`;
85100
86101
snapshot[`isRecordOf<T> > returns properly named function 2`] = `"isRecordOf((anonymous))"`;
87102
88-
snapshot[`isOptionalOf<T> > returns properly named function 1`] = `"isOptionalOf(isNumber)"`;
103+
snapshot[`isArrayOf<T> > returns properly named function 1`] = `"isArrayOf(isNumber)"`;
104+
105+
snapshot[`isArrayOf<T> > returns properly named function 2`] = `"isArrayOf((anonymous))"`;
89106
90107
snapshot[`isLiteralOneOf<T> > returns properly named function 1`] = `'isLiteralOneOf(["hello", "world"])'`;
91108
92-
snapshot[`isTupleOf<T> > returns properly named function 1`] = `
109+
snapshot[`isReadonlyUniformTupleOf<T> > returns properly named function 1`] = `"isReadonlyUniformTupleOf(3, isAny)"`;
110+
111+
snapshot[`isReadonlyUniformTupleOf<T> > returns properly named function 2`] = `"isReadonlyUniformTupleOf(3, isNumber)"`;
112+
113+
snapshot[`isReadonlyUniformTupleOf<T> > returns properly named function 3`] = `"isReadonlyUniformTupleOf(3, (anonymous))"`;
114+
115+
snapshot[`isTupleOf<T, E> > returns properly named function 1`] = `
93116
"isTupleOf([
94117
isNumber,
95118
isString,
96119
isBoolean
97-
])"
120+
], isArray)"
98121
`;
99122
100-
snapshot[`isTupleOf<T> > returns properly named function 2`] = `"isTupleOf([(anonymous)])"`;
123+
snapshot[`isTupleOf<T, E> > returns properly named function 2`] = `"isTupleOf([(anonymous)], isArrayOf(isString))"`;
101124
102-
snapshot[`isTupleOf<T> > returns properly named function 3`] = `
125+
snapshot[`isTupleOf<T, E> > returns properly named function 3`] = `
103126
"isTupleOf([
104127
isTupleOf([
105128
isTupleOf([
106129
isNumber,
107130
isString,
108131
isBoolean
109-
])
110-
])
111-
])"
112-
`;
113-
114-
snapshot[`isArrayOf<T> > returns properly named function 1`] = `"isArrayOf(isNumber)"`;
115-
116-
snapshot[`isArrayOf<T> > returns properly named function 2`] = `"isArrayOf((anonymous))"`;
117-
118-
snapshot[`isReadonlyTupleOf<T> > returns properly named function 1`] = `
119-
"isReadonlyTupleOf([
120-
isNumber,
121-
isString,
122-
isBoolean
123-
])"
124-
`;
125-
126-
snapshot[`isReadonlyTupleOf<T> > returns properly named function 2`] = `"isReadonlyTupleOf([(anonymous)])"`;
127-
128-
snapshot[`isReadonlyTupleOf<T> > returns properly named function 3`] = `
129-
"isReadonlyTupleOf([
130-
isReadonlyTupleOf([
131-
isReadonlyTupleOf([
132-
isNumber,
133-
isString,
134-
isBoolean
135-
])
136-
])
137-
])"
138-
`;
139-
140-
snapshot[`isOneOf<T> > returns properly named function 1`] = `
141-
"isOneOf([
142-
isNumber,
143-
isString,
144-
isBoolean
132+
], isArray)
133+
], isArray)
145134
])"
146135
`;
147136
148-
snapshot[`isSetOf<T> > returns properly named function 1`] = `"isSetOf(isNumber)"`;
149-
150-
snapshot[`isSetOf<T> > returns properly named function 2`] = `"isSetOf((anonymous))"`;
151-
152137
snapshot[`isObjectOf<T> > returns properly named function 1`] = `
153138
"isObjectOf({
154139
a: isNumber,
@@ -167,6 +152,29 @@ snapshot[`isObjectOf<T> > returns properly named function 3`] = `
167152
})"
168153
`;
169154
170-
snapshot[`isInstanceOf<T> > returns properly named function 1`] = `"isInstanceOf(Date)"`;
155+
snapshot[`isLiteralOf<T> > returns properly named function 1`] = `'isLiteralOf("hello")'`;
171156
172-
snapshot[`isInstanceOf<T> > returns properly named function 2`] = `"isInstanceOf((anonymous))"`;
157+
snapshot[`isLiteralOf<T> > returns properly named function 2`] = `"isLiteralOf(100)"`;
158+
159+
snapshot[`isLiteralOf<T> > returns properly named function 3`] = `"isLiteralOf(100n)"`;
160+
161+
snapshot[`isLiteralOf<T> > returns properly named function 4`] = `"isLiteralOf(true)"`;
162+
163+
snapshot[`isLiteralOf<T> > returns properly named function 5`] = `"isLiteralOf(null)"`;
164+
165+
snapshot[`isLiteralOf<T> > returns properly named function 6`] = `"isLiteralOf(undefined)"`;
166+
167+
snapshot[`isLiteralOf<T> > returns properly named function 7`] = `"isLiteralOf(Symbol(asdf))"`;
168+
169+
snapshot[`isUniformTupleOf<T> > returns properly named function 1`] = `"isUniformTupleOf(3, isAny)"`;
170+
171+
snapshot[`isUniformTupleOf<T> > returns properly named function 2`] = `"isUniformTupleOf(3, isNumber)"`;
172+
173+
snapshot[`isUniformTupleOf<T> > returns properly named function 3`] = `"isUniformTupleOf(3, (anonymous))"`;
174+
175+
snapshot[`isAllOf<T> > returns properly named function 1`] = `
176+
"isAllOf([
177+
isObjectOf({a: isNumber}),
178+
isObjectOf({b: isString})
179+
])"
180+
`;

is.ts

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ export type RecordOf<T, K extends PropertyKey = PropertyKey> = Record<K, T>;
601601
export function isRecord(
602602
x: unknown,
603603
): x is Record<PropertyKey, unknown> {
604-
if (isNullish(x) || isArray(x) || isSet(x)) {
604+
if (isNullish(x) || isArray(x) || isSet(x) || isMap(x)) {
605605
return false;
606606
}
607607
return typeof x === "object";
@@ -659,6 +659,78 @@ export function isRecordOf<T, K extends PropertyKey = PropertyKey>(
659659
);
660660
}
661661

662+
/**
663+
* Return `true` if the type of `x` is `Map<unknown, unknown>`.
664+
*
665+
* ```ts
666+
* import { is } from "https://deno.land/x/unknownutil@$MODULE_VERSION/mod.ts";
667+
*
668+
* const a: unknown = new Map([["a", 0], ["b", 1]]);
669+
* if (is.Map(a)) {
670+
* // a is narrowed to Map<unknown, unknown>
671+
* const _: Map<unknown, unknown> = a;
672+
* }
673+
* ```
674+
*/
675+
export const isMap = Object.defineProperties(isInstanceOf(Map), {
676+
name: {
677+
value: "isMap",
678+
},
679+
});
680+
681+
/**
682+
* Return a type predicate function that returns `true` if the type of `x` is `Map<K, T>`.
683+
*
684+
* To enhance performance, users are advised to cache the return value of this function and mitigate the creation cost.
685+
*
686+
* ```ts
687+
* import { is } from "https://deno.land/x/unknownutil@$MODULE_VERSION/mod.ts";
688+
*
689+
* const isMyType = is.MapOf(is.Number);
690+
* const a: unknown = new Map([["a", 0], ["b", 1]]);
691+
* if (isMyType(a)) {
692+
* // a is narrowed to Map<unknown, number>
693+
* const _: Map<unknown, number> = a;
694+
* }
695+
* ```
696+
*
697+
* With predicate function for keys:
698+
*
699+
* ```ts
700+
* import { is } from "https://deno.land/x/unknownutil@$MODULE_VERSION/mod.ts";
701+
*
702+
* const isMyType = is.MapOf(is.Number, is.String);
703+
* const a: unknown = new Map([["a", 0], ["b", 1]]);
704+
* if (isMyType(a)) {
705+
* // a is narrowed to Map<string, number>
706+
* const _: Map<string, number> = a;
707+
* }
708+
* ```
709+
*/
710+
export function isMapOf<T, K>(
711+
pred: Predicate<T>,
712+
predKey?: Predicate<K>,
713+
): Predicate<Map<K, T>> {
714+
return Object.defineProperties(
715+
(x: unknown): x is Map<K, T> => {
716+
if (!isMap(x)) return false;
717+
for (const entry of x.entries()) {
718+
const [k, v] = entry;
719+
if (!pred(v)) return false;
720+
if (predKey && !predKey(k)) return false;
721+
}
722+
return true;
723+
},
724+
{
725+
name: {
726+
get: predKey
727+
? () => `isMapOf(${inspect(pred)}, ${inspect(predKey)})`
728+
: () => `isMapOf(${inspect(pred)})`,
729+
},
730+
},
731+
);
732+
}
733+
662734
type FlatType<T> = T extends RecordOf<unknown>
663735
? { [K in keyof T]: FlatType<T[K]> }
664736
: T;
@@ -1173,6 +1245,8 @@ export default {
11731245
ReadonlyUniformTupleOf: isReadonlyUniformTupleOf,
11741246
Record: isRecord,
11751247
RecordOf: isRecordOf,
1248+
Map: isMap,
1249+
MapOf: isMapOf,
11761250
ObjectOf: isObjectOf,
11771251
Function: isFunction,
11781252
SyncFunction: isSyncFunction,

is_bench.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const cs: unknown[] = [
1010
new Set(["a", "b", "c"]),
1111
{ a: "a", b: "b", c: "c" },
1212
{ a: "a", b: 1, c: true },
13+
new Map([["a", 1], ["b", 2], ["c", 3]]),
1314
() => {},
1415
new Date(),
1516
null,
@@ -284,6 +285,35 @@ Deno.bench({
284285
},
285286
});
286287

288+
Deno.bench({
289+
name: "is.Map",
290+
fn: () => {
291+
for (const c of cs) {
292+
is.Map(c);
293+
}
294+
},
295+
});
296+
297+
Deno.bench({
298+
name: "is.MapOf<T>",
299+
fn: () => {
300+
const pred = is.MapOf(is.String);
301+
for (const c of cs) {
302+
pred(c);
303+
}
304+
},
305+
});
306+
307+
const isMapOfPred = is.MapOf(is.String);
308+
Deno.bench({
309+
name: "is.MapOf<T> (pre)",
310+
fn: () => {
311+
for (const c of cs) {
312+
isMapOfPred(c);
313+
}
314+
},
315+
});
316+
287317
const predObj = {
288318
a: is.String,
289319
b: is.Number,

0 commit comments

Comments
 (0)