Last active
January 4, 2024 22:26
-
-
Save trvswgnr/a6f6f52c2c624e9848d7c43bc57818ec to your computer and use it in GitHub Desktop.
pipe function for typescript for chaining (very similar to Rust's `Option` type)
This file contains 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
export type pipe<T> = { | |
<U>(x: T, id?: string): pipe<U>; | |
map: <U>(fn: (x: T) => U) => pipe<U>; | |
unwrap: () => NonNullable<T>; | |
unwrapOr: (defaultValue: NonNullable<T>) => NonNullable<T>; | |
unwrapOrElse: <T>(fn: () => T) => NonNullable<T>; | |
unwrapOrDefault: () => NonNullable<T>; | |
}; | |
export const pipe = <T>(x: T): pipe<T> => _pipe(x, x); | |
const _pipe = <T, Og>(x: T, og: Og): pipe<T> => { | |
return { | |
and: <U>(optb: U) => _pipe(optb, og), | |
andThen: <U>(fn: (x: T) => U) => _pipe(fn(x), og), | |
map: <U>(fn: (x: T) => U) => _pipe(fn(x), og), | |
unwrap: () => { | |
if (x === undefined || x === null) { | |
throw new Error("tried to unwrap undefined or null"); | |
} | |
return x; | |
}, | |
unwrapOr: (defaultValue: NonNullable<T>) => { | |
if (x === undefined || x === null) { | |
return defaultValue; | |
} | |
return x; | |
}, | |
unwrapOrElse: <T>(fn: () => T) => { | |
if (x === undefined || x === null) { | |
const res = fn(); | |
if (res === undefined || res === null) { | |
throw new Error("tried to unwrap undefined or null"); | |
} | |
return res as any; | |
} | |
return x as any; | |
}, | |
unwrapOrDefault: () => { | |
if (x !== undefined && x !== null) { | |
return x; | |
} | |
return getDefaultValue(og); | |
}, | |
} as any; | |
}; | |
function getDefaultValue(x: unknown) { | |
switch (typeof x) { | |
case "string": | |
return ""; | |
case "number": | |
return 0; | |
case "boolean": | |
return false; | |
case "bigint": | |
return 0n; | |
case "symbol": | |
throw new Error("no default value for symbol"); | |
case "function": | |
return () => {}; | |
case "object": | |
if (x === null) { | |
throw new Error("no default value for null"); | |
} | |
if ("default" in x) { | |
return typeof x.default === "function" ? x.default() : x.default; | |
} | |
return {}; | |
case "undefined": | |
throw new Error("no default value for undefined"); | |
default: | |
throw new Error("no default value for unknown type"); | |
} | |
} | |
// tests for pipe | |
const x = pipe(5) | |
.map((x) => x + 5) | |
.map((x) => x * 2) | |
.unwrap(); | |
console.log(x === 20 ? "pass" : "fail"); | |
const y = pipe("hello") | |
.map((x) => x.toUpperCase()) | |
.map((x) => x + "!") | |
.unwrap(); | |
console.log(y === "HELLO!" ? "pass" : "fail"); | |
const z = pipe(5) | |
.map((x) => x + 5) | |
.map((x) => x * 2) | |
.map((x) => x.toString()) | |
.unwrap(); | |
console.log(z === "20" ? "pass" : "fail"); | |
const a = pipe(Date.now()) | |
.map((x) => new Date(x)) | |
.map((x) => x.toISOString()) | |
.map((x) => x.split("T")) | |
.unwrap(); | |
console.log(a instanceof Array && a.length === 2 ? "pass" : "fail"); | |
const b = pipe("123") | |
.map(parseInt) | |
.map((x) => x + 1) | |
.map((x) => x.toString()) | |
.unwrap(); | |
console.log(b === "124" ? "pass" : "fail"); | |
// something that returns NaN | |
const mayBeUndefined = (x: number) => (x > 5 ? x : undefined); | |
const c = pipe("asdf1d23") | |
.map((x) => parseInt(x)) | |
.map(mayBeUndefined) | |
.unwrapOrDefault(); | |
console.log(b === "" ? "pass" : "fail"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment