Skip to content

Instantly share code, notes, and snippets.

@mauroc8
Last active February 8, 2022 14:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mauroc8/75dbc7786bcfb9df33577ef942f22f88 to your computer and use it in GitHub Desktop.
Save mauroc8/75dbc7786bcfb9df33577ef942f22f88 to your computer and use it in GitHub Desktop.
Type Guards
/** 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