Skip to content

Instantly share code, notes, and snippets.

@karol-majewski
Created September 8, 2019 22:24
Show Gist options
  • Save karol-majewski/237b73aaa34c7a38e44c20a3bb18a069 to your computer and use it in GitHub Desktop.
Save karol-majewski/237b73aaa34c7a38e44c20a3bb18a069 to your computer and use it in GitHub Desktop.
Composing TypeScript type guards
type TypeGuard<T = any, U extends T = any> = (candidate: T) => candidate is U;
type From<T extends TypeGuard> =
T extends TypeGuard<infer U, any>
? U
: never;
type To<T extends TypeGuard> =
T extends TypeGuard<any, infer U>
? U
: never;
declare function or<T extends TypeGuard, U extends TypeGuard>(f: T, g: U): TypeGuard<From<T | U>, To<T> | To<U>>;
declare function isString(candidate: any): candidate is string;
declare function isNumber(candidate: any): candidate is number;
const isStringOrNumber = or(isString, isNumber);
declare const input: any;
if (isStringOrNumber(input)) {
console.log(input.toString());
}
@karol-majewski
Copy link
Author

type TypeGuard<T, U extends T> = (candidate: T) => candidate is U;

type GetInputType<T extends TypeGuard<any, any>> =
  T extends TypeGuard<infer U, any>
   ? U
   : never;

type GetRefinementType<T extends TypeGuard<any, any>> =
  T extends TypeGuard<any, infer U>
   ? U
   : never;

declare function or<T extends TypeGuard<any, any>, U extends TypeGuard<any, any>>(
  f: T, g: U
): TypeGuard<GetInputType<T | U>, GetRefinementType<T> | GetRefinementType<U>>;

declare function isString(candidate: any): candidate is string;
declare function isNumber(candidate: any): candidate is number;

const isStringOrNumber = or(isString, isNumber);

declare const input: any;

if (isStringOrNumber(input)) {
    console.log(input.toString());
}

@safareli
Copy link

safareli commented Jun 2, 2021

Here is another alternative if someone is interested https://gist.github.com/safareli/8e480d72f7e2665b6030b9a08fe93f40

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment