Skip to content

Commit

Permalink
👍 Add isIncludeIn
Browse files Browse the repository at this point in the history
  • Loading branch information
Milly committed Jul 25, 2023
1 parent b377d3a commit d4b41f9
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 0 deletions.
23 changes: 23 additions & 0 deletions is.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,28 @@ export function isOptionalOf<T>(
});
}

/**
* Return a type predicate function that returns `true` if the type of `x` is included in `T`.
* This function compares value to items of the array using the *SameValueZero* algorithm.
* It is the same as `Array.prototype.includes()`.
*
* ```ts
* import is from "./is.ts";
*
* const items = [0, "a", true] as const;
* const a: unknown = "a";
* if (is.IncludeIn(items)(a)) {
* // a is narrowed to 0 | "a" | true
* const _: 0 | "a" | true = a;
* }
* ```
*/
export function isIncludeIn<T extends readonly unknown[]>(
items: T,
): Predicate<T[number]> {
return (x: unknown): x is T[number] => items.includes(x);
}

export default {
String: isString,
Number: isNumber,
Expand All @@ -324,4 +346,5 @@ export default {
Symbol: isSymbol,
OneOf: isOneOf,
OptionalOf: isOptionalOf,
IncludeIn: isIncludeIn,
};
21 changes: 21 additions & 0 deletions is_bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,24 @@ Deno.bench({
}
},
});

const includeItems = ["a", "b", "c"] as const;
Deno.bench({
name: "is.IncludeIn",
fn: () => {
const pred = is.IncludeIn(includeItems);
for (const c of cs) {
pred(c);
}
},
});

const isIncludeInPred = is.IncludeIn(includeItems);
Deno.bench({
name: "is.IncludeIn (pre)",
fn: () => {
for (const c of cs) {
isIncludeInPred(c);
}
},
});
40 changes: 40 additions & 0 deletions is_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import is, {
isBigInt,
isBoolean,
isFunction,
isIncludeIn,
isInstanceOf,
isNull,
isNullish,
Expand Down Expand Up @@ -501,6 +502,45 @@ Deno.test("isOptionalOf<T>", async (t) => {
});
});

Deno.test("isIncludeIn<T>", async (t) => {
await t.step("returns proper type predicate", () => {
const items = [0, "a", true] as const;
const a: unknown = "a";
if (isIncludeIn(items)(a)) {
type _ = AssertTrue<IsExact<typeof a, 0 | "a" | true>>;
}
});
await t.step("returns true on T includes value", () => {
const items = [0, "a", true] as const;
assertEquals(isIncludeIn(items)(0), true);
assertEquals(isIncludeIn(items)("a"), true);
assertEquals(isIncludeIn(items)(true), true);
assertEquals(isIncludeIn(items)(+0), true, "+0 is same as 0");
assertEquals(isIncludeIn(items)(-0), true, "-0 is same as 0");
});
await t.step("returns false on non of T", async (t) => {
const items = [0, "a", true] as const;
assertEquals(
isIncludeIn(items)(1),
false,
"Is a number that is not in the items",
);
assertEquals(
isIncludeIn(items)("b"),
false,
"Is a string that is not in the items",
);
assertEquals(
isIncludeIn(items)(false),
false,
"Is `false` that is not in the items",
);
await testWithExamples(t, isIncludeIn(items), {
excludeExamples: ["number", "string", "boolean"],
});
});
});

Deno.test("is", async (t) => {
const mod = await import("./is.ts");
const casesOfAliasAndIsFunction = Object.entries(mod)
Expand Down

0 comments on commit d4b41f9

Please sign in to comment.