TypeScriptでSwitch式を作る
求めてるものにたいしてはこれで十分だった
code:ts
const match = <T extends keyof any, R>(
val: T,
map: Record<T, () => R>,
): R => {
};
こう使う
code:ts
const r = match(t, {
a: () => 'a',
b: () => 'b',
});
keyが余分にあれば怒られる
keyが足りなければ怒られる
defaultみたいな曖昧なやつはいらん!って時に使える
unionで作った構造に対するパターンマッチを書きたいので全部明示することが目的
code:ts
export const match = <
R extends Record<keyof any, () => unknown>,
K extends keyof R = keyof R,
(
val: K,
map: R,
return f() as ReturnType<RK>; };
定義的には推論させる向きおかしいが、全単射なので実用上は問題ない
普通は、tの値から、a|bを推論させるのが自然だが、
{a: .. , b: .. }の方を先に見て、t: a|bを満たすようにした
Kはデフォルト値を指定できるので、1つのみ型引数を指定することができる
simple実装なのは良い
exhaustiveや、keyの重複など、型が甘い
Omitとかでkeyを絞っていけばもうちょいマシになる?
参考
上の記事でJSでIf式やSwitch式が紹介されていた
これをTypeScriptで書く
パフォーマンス的に微妙そうなので使ってない
嬉しい点
ifやswitchを式として書ける
場合によっては不要なletをconstにできる
JSX内できれいにif処理ができる
試してないが、たぶん
Switch式
code:型定義.ts
type Func<R> = () => R;
type Result<R> = { value: R } | null;
type CaseDeufalt<T, R> = {
case: Case<T, R>;
default: (defaultFunc: Func<R>) => R;
};
type Case<T, R> = (
caseVal: T
) => { then: (thenFunc: Func<R>) => CaseDeufalt<T, R> };
code:実装.ts
// switchf :: T -> ..
export const switchf = <T, R>(switchVal: T) => ({
case: caseMethod<T, R>(null)(switchVal),
});
// caseMethod :: Result -> T -> T -> Then<T>
const caseMethod = <T, R>(result: Result<R>) => (switchVal: T) => (
caseVal: T
) => ({
then: thenMethod<T, R>(result)(switchVal)(caseVal),
});
// thenMethod :: Result -> T -> T -> Func -> CaseDefault<T>
const thenMethod = <T, R>(result: Result<R>) => (switchVal: T) => (
caseVal: T
) => (thenFunc: Func<R>): CaseDeufalt<T, R> => {
const hasResult = result !== null || switchVal !== caseVal;
const rlt: Result<R> = hasResult ? result : { value: thenFunc() };
return {
case: caseMethod<T, R>(rlt)(switchVal),
default: defaultMethod(rlt),
};
};
// defaultMethod :: Result -> Func -> ReturnValue
const defaultMethod = <R>(result: Result<R>) => (defaultFunc: Func<R>): R =>
result?.value ?? defaultFunc();
こんな感じで使う
code:ts
const dayNumber = 0;
const dayMessage = switchf<number, string>(dayNumber)
.case(0).then(() => "日曜日")
.case(1).then(() => "月曜日")
.case(2).then(() => "火曜日")
.case(3).then(() => "水曜日")
.case(4).then(() => "木曜日")
.case(5).then(() => "金曜日")
.case(6).then(() => "土曜日")
.default(() => "error");
Tってわかりにくいな
switch(ここ)に該当するものの型
Caseとかかな
RはResult
懸念ポイント
breakしない系のswtichにも対応しているか?
never型での漏れ防止が使えるか
これもうこの関数内に書けばいい
ただ、本当にdefaultがほしいこともあると思う
なので、
default節がないときはneverチェック
あるときはしない
という実装で良いと思う