Skip to content

Instantly share code, notes, and snippets.

@karol-majewski
Last active August 7, 2020 12:33
Show Gist options
  • Save karol-majewski/285a1db731103d01d61444ae3cfa699f to your computer and use it in GitHub Desktop.
Save karol-majewski/285a1db731103d01d61444ae3cfa699f to your computer and use it in GitHub Desktop.
Type-safe refinements (type guards) in TypeScript (moved to https://github.com/karol-majewski/refinements)
namespace Refinement {
class Hit<T> {
constructor(readonly value: T) {}
}
class Miss {}
type Result<T> = Hit<T> | Miss;
export function hit<T> (value: T) {
return new Hit(value);
}
export const miss = new Miss();
export function create<T, U extends T>(refine: (candidate: T) => Result<U>): (candidate: T) => candidate is U {
return (candidate): candidate is U => refine(candidate) instanceof Hit;
}
}
@karol-majewski
Copy link
Author

karol-majewski commented Jun 18, 2020

With regular type guards, it's easy to make a mistake. The body of user-defined type guards is not type-checked.

class Cat {
  meow(): void {}
}

class Dog {
  bark(): void {}
}

type Pet = Cat | Dog;

const isCat = (pet: Pet): pet is Cat =>
  pet instanceof Dog; // Bad!

console.log(
  isCat(new Cat()), // false ⛔️
  isCat(new Dog()) // true ⛔️
)

The implementation of isCat is incorrect, but TypeScript doesn't care.

TypeGuard.create solves that problem.

@karol-majewski
Copy link
Author

@karol-majewski
Copy link
Author

Type guards use cases: use for filter and discrimination.

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