0xf

日記だよ

配列とかオブジェクトリテラルを as const するとルックアップ型で値が利用できる

as constすると、配列とかオブジェクトリテラルはそのものズバリの型になる。type MyType = ["hello", "world"] as conststring[]ではなく、["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()