Home > database >  Invoke functions with different arguments base on object key
Invoke functions with different arguments base on object key

Time:01-17

So I have the following snippet

interface base {
    name: string;
}
interface base2 {
    title: string;
}

const func1 = (arg: base) => {
    return arg.name;
}

const func2 = (arg: base2) => {
    return arg.title;
}

const formatter: Record<string, (arg: base | base2) => string> = {
    format: func1,
    format2: func2
}

formatter["format"]({ name: "test" });

The two functions have different argument types and I am trying to invoke them as a result of the return value of the object.

But I am getting a type error on the functions

Is there a way to abstractly call the functions and not produce type errors

CodePudding user response:

(arg: base | base2) => string is a function that can handle both a base and a base2 argument. func1 and func2 can't do this, they can handle either base or base2, so a call formatter["format"]({ title: "test" }); is valid, but will cause func1 to misbehave (in this case return undefined when it should return string)

You probably want a union of functions ((arg: base2) => string) | ((arg: base) => string). This will let you assign func1 or func2 to the properties of formatter but since an arbitrary key can be either function (and TS doesn't know which one) you can't call formatter["format"] with either base or base2, you need to call with something that would be valid for either function signature, so the argument would have to be the intersection base & base2:

const formatter: Record<string, ((arg: base2) => string) | ((arg: base) => string)> = {
    format: func1,
    format2: func2
}

formatter["format"]({ name: "test" });// Not ok, what if format is (arg: base2) => string)
formatter["format"]({ title: "test" });// Not ok, what if format is (arg: base) => string)
formatter["format"]({ title: "test", name: "" });// Ok, either function will get what it needs

Playground Link

Now this is probably not what you want. If the keys are known and you are using the type annotation just to constrain that all properties have one of the known function signatures, you could use the satisfies operator in typescript 4.8 instead. This will check that the object respects the object type while preserving the actual property types:

const formatter = {
    format: func1,
    format2: func2
} satisfies Record<string, ((arg: base2) => string) | ((arg: base) => string)> 

formatter["format"]({ name: "test" });// Ok, actual type is known

Playground Link

Or if you are not using a version that supports satisfies yet you can use a function to do a similar constraint: Playground Link

  • Related