Home > Software design >  How to properly type a function that is mapped back through several other objects?
How to properly type a function that is mapped back through several other objects?

Time:02-03

Edit

requestedFunctionAlias is dynamic.

For instance,

const getFn = ({ requestedFunctionAlias }) => {
    return FUNCTIONS_MATRIX[FUNCTION_ALIASES[requestedFunctionAlias]];
}

const fn = getFn({ requestedFunctionAlias: 'a1' });

fn({ age: 30 })

Suppose the following,

const myFunction1 = ({ name }: { name: string }) => console.log('name', name);
const myFunction2 = ({ age }: { age: number }) => console.log('age', age);
const myFunction3 = ({ hobbies }: { hobbies: string[] }) => console.log('hobbies', hobbies);

const FUNCTION_ALIASES = {
  a1: 'myFunction1',
  a2: 'myFunction2',
  a3: 'myFunction3',
};

const FUNCTIONS_MAP = {
  myFunction1: 'a1',
  myFunction2: 'a2',
  myFunction3: 'a3',
};


const FUNCTIONS_MATRIX = {
  [FUNCTION_ALIASES[FUNCTIONS_MAP.myFunction1]]: myFunction1,
// ^^^ Element implicitly has an 'any' type 
  [FUNCTION_ALIASES[FUNCTIONS_MAP.myFunction2]]: myFunction2,
// ^^^ Element implicitly has an 'any' type 
  [FUNCTION_ALIASES[FUNCTIONS_MAP.myFunction3]]: myFunction3,
// ^^^ Element implicitly has an 'any' type 
};

const requestedFunctionAlias: string = 'a1';

const fn = FUNCTIONS_MATRIX[FUNCTION_ALIASES[requestedFunctionAlias]];
//                          ^^^^ Element implicitly has an 'any' type

fn({ name: 'Michael!' });
//  ^^^^^^ Property 'age' is missing in type '{ name: string; }' but required in type '{ age: number; }'

I can solve some of these errors like this,

const FUNCTIONS_MATRIX = {
  [FUNCTION_ALIASES[FUNCTIONS_MAP.myFunction1 as keyof typeof FUNCTION_ALIASES]]: myFunction1,
  [FUNCTION_ALIASES[FUNCTIONS_MAP.myFunction2 as keyof typeof FUNCTION_ALIASES]]: myFunction2,
  [FUNCTION_ALIASES[FUNCTIONS_MAP.myFunction3 as keyof typeof FUNCTION_ALIASES]]: myFunction3,
};

const fn = FUNCTIONS_MATRIX[FUNCTION_ALIASES[requestedFunctionAlias as keyof typeof FUNCTION_ALIASES]];

But this seems messy and it still doesn't solve the error at fn({ name: 'Michael!' });.

What can I do here?

CodePudding user response:

First declare FUNCTION_ALIASES and FUNCTIONS_MAP as const. With this you tell typescript that their type is narrower than key: string.

const FUNCTION_ALIASES = {
  a1: "myFunction1",
  a2: "myFunction2",
  a3: "myFunction3",
} as const;

const FUNCTIONS_MAP = {
  myFunction1: "a1",
  myFunction2: "a2",
  myFunction3: "a3",
} as const;

Then remove type declaration string from requestedFunctionAlias, since it loses type information - it's type is literal "a1" and typescript needs it, to know at type checking time exactly which function will be returned:

const requestedFunctionAlias = "a1";

Edit: With function overloading you can kinda handle dynamic argument:

const myFunction1 = ({ name }: { name: string }) => console.log("name", name);
const myFunction2 = ({ age }: { age: number }) => console.log("age", age);
const myFunction3 = ({ hobbies }: { hobbies: string[] }) =>
  console.log("hobbies", hobbies);

function myFunction(which: "a1", arg: Parameters<typeof myFunction1>[0]): void;
function myFunction(which: "a2", arg: Parameters<typeof myFunction2>[0]): void;
function myFunction(which: "a3", arg: Parameters<typeof myFunction3>[0]): void;
function myFunction(
  which: "a1" | "a2" | "a3",
  arg: any
) {
  switch (which) {
    case "a1":
      return myFunction1(arg);

    case "a2":
      return myFunction2(arg);

    case "a3":
      return myFunction3(arg);

    default:
      break;
  }
}


myFunction("a1", { name: "Michael!" });
  • Related