If you are working with typescript, sooner or later you will encounter an issue with Array.prototype.includes
const PROPS = ['a', 'b', 'c'] as const; PROPS.includes('d') // error const includes = (elem: string) => PROPS.includes(elem) // error
I don't want to dig into type theory problem of this issue. I just will provide you with curried generic solution.
const PROPS = ['a', 'b', 'c'] as const; const withTuple = < List extends string[] >(list: readonly [...List]) => (prop: string): prop is List[number] => list.includes(prop) const includes = withTuple(PROPS); const result = includes('d') declare let str: string if (includes(str)) { str // "a" | "b" | "c" }
However, it is still not cool. Our function works only with strings. What if we have a list of numbers or other primitives ?
First of all, we need to create utility type which will be able to convert literal type to to more wider type. I mean, it should convert literal type of 42 to number
type Primitives = | string | number | bigint | boolean | symbol | null | undefined type InferPrimitive<T, P> = P extends any ? T extends P ? P : never : never; type Inference<T> = InferPrimitive<T, Primitives> { type _ = Inference<2 | 's'> // stirng | number type __ = Inference<42> // number }
Now we can use our type with curried function
type Primitives = | string | number | bigint | boolean | symbol | null | undefined type InferPrimitive<T, P> = P extends any ? T extends P ? P : never : never; type Inference<T> = InferPrimitive<T, Primitives> { type _ = Inference<2 | 's'> // stirng | number type __ = Inference<42> // number } const PROPS = ['a', 'b', 'c'] as const; const withTuple = < List extends Primitives[] >(list: readonly [...List]) => (prop: Inference<List[number]>): prop is Inference<List[number]> & List[number] => list.includes(prop) const includes = withTuple(PROPS); includes(2) // expected error includes(['str']) // expected error const result = includes('d') // ok declare let str: string if (includes(str)) { str // "a" | "b" | "c" }
As you might have noticed, TS allows you to call includes
only with strings
.
That's all.
Top comments (0)