Moduleis-one-of

Utilities for determining whether a value is in a given Array/Set.

JavaScript Array.includes and Set.has determine whether a value is in a given Array/Set. They are typed such that the parameter must be assignable to the element type of the Array/Set:

const fruits = ["apple", "orange", "grape"];  // or new Set(["apple", "orange", "grape"])
const fireball: string = "fire ball";
const containingFireball = fruits.includes(fireball); // or fruits.has(fireball); OK
const containingNumber1 = fruits.includes(1); // or fruits.has(1);
// error: Argument of type 'number' is not assignable to parameter of type 'string'.

This makes sense, because when fruits is dynamically constructed by the logic of the program, an unmatched element type is not intended and likely an error. However, the third line in the above code snippet would fail to compile if fruits contains literal types:

const fruits = ["apple", "orange", "grape"] as const;  // or new Set(["apple", "orange", "grape"] as const);
const fireball: string = "fire ball";
const containingFireball = fruits.includes(fireball); // or fruits.has(fireball);
// error: Argument of type 'string' is not assignable to parameter of
// type '"apple" | "orange" | "grape"'.

This doesn't make sense because the semantics has changed: fireball doesn't have to have one of the literal string types in fruits.

To address this issue, this module provides a isOneOf function that is typed to accommodate the second scenario above, while retains the same logic as Array.includes and Set.has:

const fruits = ["apple", "orange", "grape"] as const;  // or new Set(["apple", "orange", "grape"] as const);
const fireball: string = "fire ball";
const containsFireball = isOneOf(fireball, fruits); // OK

For the first scenario, you should continue using Array.includes and Set.has.

This function can also be used as a type guard. Continuing the previous example:

if (isOneOf(fireball, fruits)) {
// In this block, fireball is of the type "apple" | "orange" | "grape"
}

Index

Functions