Last active
February 8, 2022 14:34
-
-
Save mauroc8/75dbc7786bcfb9df33577ef942f22f88 to your computer and use it in GitHub Desktop.
Type Guards
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** A `TypeGuard` is a function that checks that an unknown value is of certain type. */ | |
export type TypeGuard<A> = | |
(value: unknown) => value is A; | |
const number: TypeGuard<number> = (value: unknown): value is number => | |
typeof value === 'number'; | |
const string: TypeGuard<string> = (value: unknown): value is string => | |
typeof value === 'string'; | |
const null_: TypeGuard<null> = (value: unknown): value is null => | |
value === null; | |
const boolean: TypeGuard<boolean> = (value: unknown): value is boolean => | |
typeof value === 'boolean'; | |
function literal<Literal extends string | number | boolean | null | undefined>(literal: Literal): TypeGuard<Literal> { | |
return (value: unknown): value is Literal => | |
value === literal; | |
} | |
function array<A>(typeGuard: TypeGuard<A>): TypeGuard<Array<A>> { | |
return (value: unknown): value is Array<A> => | |
value instanceof Array | |
&& value.every(typeGuard); | |
} | |
function object<A>(shape: { [key in keyof A]: TypeGuard<A[key]> }): TypeGuard<A> { | |
return (value: unknown): value is A => | |
typeof value === 'object' | |
&& value !== null | |
&& Object.entries(shape).every(([key, typeGuard]) => | |
key in value | |
&& (typeGuard as any)(value[key]) | |
); | |
} | |
type ToUnion<TypeGuards> = | |
TypeGuards extends [TypeGuard<infer First>, ...infer Others] | |
? First | ToUnion<Others> | |
: never; | |
function union< | |
TypeGuards extends TypeGuard<any>[] | |
>( | |
...args: TypeGuards | |
): TypeGuard<ToUnion<TypeGuards>> { | |
return (value: unknown): value is ToUnion<TypeGuards> => | |
args.some(typeGuard => (typeGuard as any)(value)); | |
} | |
type ToTuple<TypeGuards> = | |
TypeGuards extends [TypeGuard<infer First>, ...infer Others] | |
? [First, ...ToTuple<Others>] | |
: []; | |
function tuple< | |
TypeGuards extends TypeGuard<any>[] | |
>( | |
...args: TypeGuards | |
): TypeGuard<ToTuple<TypeGuards>> { | |
return (value: unknown): value is ToTuple<TypeGuards> => | |
value instanceof Array | |
&& args.every((typeGuard, index) => typeGuard(value[index])); | |
} | |
export default { | |
number, | |
string, | |
null_, | |
boolean, | |
literal, | |
array, | |
object, | |
union, | |
tuple | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment