I have the following code:
export type FooParams = {
foo1: { x: number };
foo2: { y: string };
};
export type FooKey = keyof FooParams; // or export type FooKey = "foo1" | "foo2";
export interface FooAction<T extends FooKey> {
execute: (params: FooParams[T]) => void;
}
const foo1Action: FooAction<"foo1"> = {
execute: (params) => {
console.log(params.x);
},
};
const foo2Action: FooAction<"foo2"> = {
execute: (params) => {
console.log(params.y);
},
};
export const fooActions: Record<FooKey, FooAction<FooKey>> = {
foo1: foo1Action,
foo2: foo2Action,
};
I can't strongly type the variable fooActions, in order to force a FooAction for every FooKey. In the above example I have the following error.
Type 'FooAction<"foo1">' is not assignable to type 'FooAction<keyof FooParams>'.
Type 'keyof FooParams' is not assignable to type '"foo1"'.
Type '"foo2"' is not assignable to type '"foo1"'.ts(2322)
Any idea how to correctly declare the fooActions type?
CodePudding user response:
FooAction<FooKey> means execute will have type (params: FooParams[FooKey]) => void which in turns would resolve to (params: { x: number } | { y: string }) => void;. This means it will have to be a function that handle both { x: number } and { y: number }. So all values of an object of type Record<FooKey, FooAction<FooKey>> would have to handle both types, while foo1Action and foo2Action can only handle one type or the other.
You want to type fooActions with a correlation between the key and the type. You can do that with a custom mapped type:
type FooActionMap = {
[P in FooKey]: FooAction<P>
}
export const fooActions: FooActionMap = {
foo1: foo1Action,
foo2: foo2Action,
};
