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.
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