tsでmatch式
モチベーション
複雑なmatcherは要らない
十分成熟してるし使えばええんやろけども
gzippedで2.4kBあるし、削りたい人にとっても良いかも
code:ts
type GuardedType<T> = T extends (x: any) => x is infer U ? U : never;
type IsNever<T> = T extends never ? true : false; interface NonExhaustiveError<i> {}
interface When<Input, Output> {
<
Pred extends (v: Input) => v is any,
Expected extends GuardedType<Pred> & Input,
Then extends (matched: Expected) => unknown
(
pred: (v: Input) => v is Expected,
then: Then
): Chain<Exclude<Input, Expected>, Output | ReturnType<Then>>;
}
type Chain<Input, Output> = {
when: When<Input, Output>;
otherwise: <A>(fn: (v: Input) => A) => Output | A;
exhaustive: IsNever<Input> extends true
? () => Output
: NonExhaustiveError<Input>;
};
const alreadyMatched = <Input, Output>(val: Output): Chain<Input, Output> => ({
when: (_pred, _then) => alreadyMatched(val),
otherwise: () => val,
exhaustive: (() => val) as any,
});
const chain = <Input, Output>(val: Input): Chain<Input, Output> => ({
when: (pred, then) => {
type Pred = typeof pred;
type Then = typeof then;
type Expected = GuardedType<Pred>;
if (pred(val)) {
const thenValue = then(val satisfies Expected) as ReturnType<Then>;
return alreadyMatched(thenValue);
} else {
return chain(val as Exclude<Input, Expected>);
}
},
otherwise: (fn) => fn(val),
exhaustive: (() => {
throw new Error();
}) as any,
});
export const match = <Input>(v: Input) => chain<Input, never>(v);
usage
code:ts
import { match } from "./index";
declare const hoge: 1 | 2 | 3;
const result = match(hoge)
.when(
(v): v is 1 => v === 1,
(v) => "A" as const
)
.when(
(v): v is 2 => v === 2,
(v) => "B" as const
)
.when(
(v): v is 3 => v === 3,
(v) => "C" as const
)
// .otherwise((v) => "default" as const)
.exhaustive();
result.startsWith("a");
when({name:'a'}, (nameIsA) => {})
method chainじゃなくてなにかモジュラーな実装も考えたい。valibot的な