as const
すると、配列とかオブジェクトリテラルはそのものズバリの型になる。type MyType = ["hello", "world"] as const
は string[]
ではなく、["hello", "world"]
という型となる。
するとルックアップ型で参照して派生型が作れるねという話。
type Output<T extends { name: string }[]> = { [K in T[number]['name']]: boolean }; function create<T extends { name: string }[]>(params: T): Output<T> { return params.reduce((acc, param) => ({ ...acc, [param.name]: false }), {} as Output<T>); } // これが通る const r: { foobar: boolean, aaaa:boolean} = create([{"name": "foobar"}, {name:"aaaa"}] as const)
どういう時に嬉しいかというと、
に書いたようなことをする際、
type ActionVariant = "ready"| "start" | "done" | "cancel"; type StateVariant = "todo" | "doing" | "done" | "cancel"; type StateMachineRule = { state: StateVariant, action: ActionVariant , next: StateVariant } type StateMachine<T extends StateMachineRule[]> = { [K in T[number]['action']]: () => StateVariant } & { currentState: () => StateVariant}; function createStateMachine<T extends StateMachineRule>(initialState: StateVariant, rules: T[]): StateMachine<T[]> { let currentState: StateVariant = initialState; return rules.reduce((acc, rule) => ({ ...acc, [rule.action]: () => { const r = rules.find(l => l.state === currentState && l.action === rule.action) if(!r) return currentState; currentState = r.next; return currentState; } }), { currentState: () => currentState } as StateMachine<T[]>) }
こういう定義が可能になる。
例えば以下のようなルール定義をしたとする。
const machine = createStateMachine("todo", [ {state: "todo", action:"start", next: "doing"}, {state: "doing", action:"start", next: "doing"}, {state: "doing", action:"done", next: "done"}, {state: "done", action:"done", next: "done"}, {state: "doing", action:"cancel", next: "cancel"}, {state: "done", action:"ready", next: "todo"}, {state: "cancel", action:"ready", next: "todo"} ] as const)
このとき、machineは以下のようなメンバを持つようになる。便利だ。
machine.start() machine.done() machine.cancel() machine.ready() const state = machine.currentState()