ross@normalcool
2026-01-03
TypeScript Result Proto
I get why error catches in TS are unknown but i sure don’t like working with it. Also find try{}catch(e){} to just be annoying in general. Not uncommon seeing multiple places within a try that could fail. I like making function results more certain
interface ResultOk<T> {
kind: "ok";
value: T;
isOk(): this is ResultOk<T>;
isErr(): this is ResultErr;
match<U>(handlers: {
ok: (value: T) => U;
err: (err: ResultErr["err"]) => U;
}): U;
unwrap(): T;
unwrapErr(): never;
}
const okProto = {
isOk(): this is ResultOk<any> {
return true;
},
isErr(): this is ResultErr {
return false;
},
match<U>(this: ResultOk<any>, handlers: any): U {
return handlers.ok(this.value);
},
unwrap(this: ResultOk<any>) {
return this.value;
},
unwrapErr(): never {
throw new Error("Called unwrapErr on Ok");
},
};
const ok = <T>(value: T): ResultOk<T> => {
const o = Object.create(okProto);
o.value = value;
o.kind = "ok";
return o;
};interface ResultErr {
kind: "err";
err: {
tag: ErrTags;
msg: string;
metadata?: Record<string, any>;
};
isOk(): this is ResultOk<never>;
isErr(): this is ResultErr;
match<U>(handlers: {
ok: (value: never) => U;
err: (err: ResultErr["err"]) => U;
}): U;
unwrap(): never;
unwrapErr(): ResultErr["err"];
}
const errProto = {
isOk(): this is ResultOk<never> {
return false;
},
isErr(): this is ResultErr {
return true;
},
match<U>(this: ResultErr, handlers: any): U {
return handlers.err(this.err);
},
unwrap(): never {
throw new Error("Called unwrap on Err");
},
unwrapErr(this: ResultErr) {
return this.err;
},
};
const err = <T>(err: Err): ResultErr => {
const o = Object.create(errProto);
o.err = err;
o.kind = "err";
return o;
};