Home > Back-end >  Applying a generic to a generic?
Applying a generic to a generic?

Time:08-04

I'm trying to enforce typings to match expected inputs of a function in a record, I have the following:

type ABC = {
    money: number;
}

type Inputs = {
    'abc': ABC
}

type Keyed = keyof Inputs

type ActionFunc = <T extends Keyed>(T, arg: Inputs[T]) => string;

const ActionMappings: Record<Keyed, ActionFunc>  = {
    abc: (inputIsNotDefinedAsABC) => {
        return 'I would like it to be defined'
    }
}

Typescript is not enforcing that the input for ABC is type 'ABC' and instead infers it as an 'any'.

Have tried a few variations of the above but the fundamental problem I think is that I haven't worked out how to get TS to understand the generic function should be constrained in the Record.

The only way I can get it to work is to be explicit:

const ActionMappings  = {
    abc: (definedAsABC: ABC) => {
        return 'kind of works'
    }
}

ActionMappings[abc](/* accepts payload only in ABC type*/)

CodePudding user response:

From your use case it looks like you want:

type ActionFunc<K extends Keyed> = (arg: Inputs[K]) => string;

const ActionMappings: { [K in Keyed]: ActionFunc<K> } = {
    abc: (x) => {
        // (parameter) x: ABC     
        return x.money.toFixed(2); // okay
    }
}

Compare my version type ActionFunc<K extends Keyed> = (arg: Inputs[K]) => string to (a modified/corrected rendition of) your version type OrigActionFunc = <K extends Keyed>(arg: Inputs[K]) => string. You can see that both involve a generic type parameter K, but the scopes of the type parameters differ.

In my version, ActionFunc<K> is itself generic. That is, you can't talk about ActionFunc without specifying K. Once you do specify K (e.g., as "abc"), then ActionFunc<"abc"> is not a generic function type (e.g., (arg: ABC) => string). You want each member of type K of the ActionMappings object to accept only the corresponding argument of type Inputs[K] (note that this leads naturally to a mapped type over every key K in Keyed). You want to be in charge of K, not callers of the functions.

This differs from your version in which OrigActionFunc was a specific type (no type parameter necessary to talk about it) corresponding to a generic function, where callers specify the type parameter, not you. Such a function would need to accept every possible type parameter; you'd have no control over which argument type was passed into the functions.

Playground link to code

CodePudding user response:

Hope it helps you

type ABC = {
  money: number;
};

type Inputs = {
  abc: ABC;
};

type ActionMappingsType = {
  [K in keyof Inputs]: (arg: Inputs[K]) => String;
};

const ActionMappings: ActionMappingsType = {
  abc: (arg: ABC) => {
    return 'ABC';
  },
};

const abc = ActionMappings.abc({ money: 1 });

console.log(abc);
//ABC
  • Related