Skip to content

Commit 5c0aa28

Browse files
committed
👍 Add isOneOf<T>
1 parent 3626407 commit 5c0aa28

File tree

2 files changed

+51
-0
lines changed

2 files changed

+51
-0
lines changed

is.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,30 @@ export function isNullish(x: unknown): x is null | undefined {
178178
return x == null;
179179
}
180180

181+
export type OneOf<T> = T extends (infer U)[]
182+
? T extends Predicate<infer U>[] ? U : T
183+
: T;
184+
185+
/**
186+
* Return a type predicate function that returns `true` if the type of `x` is `OneOf<T>`.
187+
*
188+
* ```ts
189+
* import is from "./is.ts";
190+
*
191+
* const preds = [is.Number, is.String, is.Boolean];
192+
* const a: unknown = { a: 0, b: "a", c: true };
193+
* if (is.OneOf(preds)(a)) {
194+
* // a is narrowed to number | string | boolean;
195+
* const _: number | string | boolean = a;
196+
* }
197+
* ```
198+
*/
199+
export function isOneOf<T extends readonly Predicate<unknown>[]>(
200+
preds: T,
201+
): Predicate<OneOf<T>> {
202+
return (x: unknown): x is OneOf<T> => preds.some((pred) => pred(x));
203+
}
204+
181205
export default {
182206
String: isString,
183207
Number: isNumber,
@@ -192,4 +216,5 @@ export default {
192216
Null: isNull,
193217
Undefined: isUndefined,
194218
Nullish: isNullish,
219+
OneOf: isOneOf,
195220
};

is_test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import is, {
1111
isNullish,
1212
isNumber,
1313
isObjectOf,
14+
isOneOf,
1415
isRecord,
1516
isRecordOf,
1617
isString,
@@ -32,6 +33,7 @@ Deno.test("is defines aliases of functions", () => {
3233
assertStrictEquals(is.Null, isNull);
3334
assertStrictEquals(is.Undefined, isUndefined);
3435
assertStrictEquals(is.Nullish, isNullish);
36+
assertStrictEquals(is.OneOf, isOneOf);
3537
});
3638

3739
Deno.test("isString", async (t) => {
@@ -277,3 +279,27 @@ Deno.test("isNullish", async (t) => {
277279
assertEquals(isNullish(function () {}), false);
278280
});
279281
});
282+
283+
Deno.test("isOneOf<T>", async (t) => {
284+
await t.step("returns true on one of T", () => {
285+
const preds = [isNumber, isString, isBoolean];
286+
assertEquals(isOneOf(preds)(0), true);
287+
assertEquals(isOneOf(preds)("a"), true);
288+
assertEquals(isOneOf(preds)(true), true);
289+
});
290+
await t.step("returns false on non of T", () => {
291+
const preds = [isNumber, isString, isBoolean];
292+
assertEquals(isOneOf(preds)([]), false);
293+
assertEquals(isOneOf(preds)({}), false);
294+
assertEquals(isOneOf(preds)(function () {}), false);
295+
assertEquals(isOneOf(preds)(null), false);
296+
assertEquals(isOneOf(preds)(undefined), false);
297+
});
298+
await t.step("returns proper type predicate", () => {
299+
const preds = [isNumber, isString, isBoolean];
300+
const a: unknown = [0, "a", true];
301+
if (isOneOf(preds)(a)) {
302+
const _: number | string | boolean = a;
303+
}
304+
});
305+
});

0 commit comments

Comments
 (0)