Skip to content

Instantly share code, notes, and snippets.

@trvswgnr
Last active January 4, 2024 22:26
Show Gist options
  • Save trvswgnr/a6f6f52c2c624e9848d7c43bc57818ec to your computer and use it in GitHub Desktop.
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)
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