Created
September 9, 2025 17:04
-
-
Save unitario/fdaf00c20f364c6909a857e9910965a8 to your computer and use it in GitHub Desktop.
A tiny curried TypeScript type guard
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
| /** | |
| * Maps a reference type to its runtime value type. | |
| */ | |
| type ValueTypeOf<T> = T extends | |
| | StringConstructor | |
| | NumberConstructor | |
| | BooleanConstructor | |
| | SymbolConstructor | |
| | BigIntConstructor | |
| ? ReturnType<T> | |
| : T extends new ( | |
| ...args: unknown[] | |
| ) => unknown | |
| ? InstanceType<T> | |
| : T | |
| /** | |
| * Runtime type guard that checks if `value` matches the given `reference`. | |
| * | |
| * `reference` can be: | |
| * - A primitive constructor: `String`, `Number`, `Boolean`, `Symbol`, `BigInt` | |
| * - A class/constructor | |
| * - A primitive value (e.g., `"foo"`, `123`, `null`, `undefined`) | |
| * | |
| * Two call forms: | |
| * | |
| * 1) Binary: | |
| * `is(reference, value): value is ValueTypeOf<typeof reference>` | |
| * | |
| * 2) Curried: | |
| * `is(reference)(value): value is ValueTypeOf<typeof reference>` | |
| * | |
| * @typeParam T Reference type used to determine the predicate’s narrowed type. | |
| * | |
| * @example | |
| * is(String, "string") // returns true | |
| * is("string", "string") // returns true | |
| * is("string", String) // returns true | |
| * is(Number, 1234) // returns true | |
| * is(Boolean, true) // returns true | |
| * is(Array, []) // returns true | |
| * is(Class, Class) // returns true | |
| * is(Class, new Class()) // returns true | |
| * is(ParentClass, ChildClass) // returns true | |
| * | |
| * is(String, 1234) // returns false | |
| * is(1234, "1234") // returns false | |
| * | |
| * [1, 2, 3].every(is(Number)) // returns true | |
| * [1, 2, "3"].every(is(Number)) // returns false | |
| * | |
| * @remarks | |
| * - By design, this compares constructor/function names and prototype chains. | |
| * If two unrelated classes share the same name, they are considered a match. | |
| */ | |
| function is<T>(reference: T, value: unknown): value is ValueTypeOf<T>; | |
| function is<T>(reference: T): (value: unknown) => value is ValueTypeOf<T>; | |
| function is<T>(reference: T, ...args: [] | [unknown]) { | |
| const getPrototypeName = (value: unknown) => { | |
| if (value instanceof Function) { | |
| return value.name | |
| } | |
| if (value instanceof Object) { | |
| return value.constructor.name | |
| } | |
| return Object.prototype.toString.call(value).slice(8, -1) | |
| } | |
| if (args.length === 0) { | |
| return (value: unknown) => is(reference, value) | |
| } | |
| return ( | |
| getPrototypeName(reference) === getPrototypeName(args[0]) || | |
| (args[0] instanceof Object && is(reference, Object.getPrototypeOf(args[0]))) | |
| ) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment