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)