Home > Mobile >  typescript template literal type with union all method
typescript template literal type with union all method

Time:01-12

i need to union of all my key and method from object to a an enum

// i want that result [ 'aStore.aMethod1','aStore.aMethod2', 'bStore.bMethod1', 'bStore.bMethod2' ,''bStore.bMethod3'] sample

const objA = {
  aMethod1: function (_params: string) {
    console.log("obj A a method1")
    return 'A1'
  },
  aMethod2: function (_params: string, _param2: string) {
    console.log("obj A a method2")
    return 'A2'
  }
}
const objB = {
  bMethod1: function (_params: string) {
    console.log("obj b  bmethod1")
    return 'B1'
  },
  bMethod2: function (_params: string, _param2: string) {
    console.log("obj B bmethod2")
    return 'B2'
  },
  bMethod3: function (_params: string, _param2: string) {
    console.log("obj B bmethod3")
    return 'B3';
  }
}

const rootStore = {
  aStore: objA,
  bStore: objB,
}

// https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html
type actionName<T> = `${string & keyof T}.${'HELP'}`; 
///  how do i make typesafe here
// i want an action a list of [ 'aStore.aMethod1','aStore.aMethod2', 'bStore.bMethod1', 'bStore.bMethod2' ,''bStore.bMethod3']

// is there any way to make args type safe too.
function dynamicCall<T> (action: actionName<T>,...args:any) {
  // it will call objA
  const [storeName, method] = action.split('.');
  const store= (rootStore as any)[storeName];
  if(store&&store[method]){
     (store as any)[method].apply(undefined,args)
  }
}
dynamicCall<typeof rootStore>('aStore.aMethod1')

playground link

it will perfect if you can help to make an args typesafe too. Thank you

CodePudding user response:

You can use a mapped type to go through all the properties on the root type and build the paths:

type actionName<T> = {
  [K in keyof T & string]: `${K}.${keyof T[K] & string}`; 
}[keyof T & string]

Playground Link

You can also get the parameters and the return type to type check:

type ActionName<T> = {
  [K in keyof T & string]: `${K}.${keyof T[K] & string}`; 
}[keyof T & string]

type GetMethod<T, M> = M extends `${infer K}.${infer SubKey}` ? 
  T extends Record<K, Record<SubKey, infer Method extends (...a: any) => any>> ? Method: never: never;

function dynamicCall<T>(store: T) {
  return <M extends ActionName<T>>(action: ActionName<T>,...args: Parameters<GetMethod<T, M>>): ReturnType<GetMethod<T, M>> => {
    const [storeName, method] = action.split('.');
    const store= (rootStore as any)[storeName];
    if(store&&store[method]){
      return (store as any)[method].apply(undefined,args)
    }
    throw new Error("Method not found")
  }
}

Playground Link

  • Related