I'm trying to type a function with a rather complex type signature. It accepts two dictionaries args and funcs, where funcs maps keys to functions that transform type A to B, and args maps a superset of those keys to: A if the key occurs in funcs, and to B otherwise. I.e., if a function is defined for a certain key, use the function to map the value from the same key from the other dictionary, if no corresponding function exists just use the value itself.
I've got the following so far, but that doesn't seem to work:
function foo<Properties>(
args: { [P in keyof typeof funcs]: Parameters<typeof funcs[P]>[0] } & {
[P in Exclude<keyof Properties, keyof typeof funcs>]?: Properties[P];
},
funcs: { [P in keyof Properties]?: (attributes: any) => Properties[P] }
) {}
interface Bar {
x: number;
y: string;
}
foo<Bar>(
{
x: "a",
y: 1,
},
{
y: (a: string) => "a",
}
);
In this case, I would expect to see two errors: one indicating that x should map to a number in the first dictionary, and the second indicating that y should be a string in the first dictionary. It seems I'm overestimating the power of TS - is this intended behavior even possible?
Interesting problem!
I think this achieves your intended outcome:
type Func<I, O> = (arg: I) => O
type Funcs<T> = { [K in keyof Partial<T>]: Func<K, T[K]> }
declare function converter<Properties>(
args: Properties,
funcs: Funcs<Properties>
): void;
interface Bar {
x: number;
y: string;
}
converter<Bar>(
{
x: 1,
y: "foo",
},
{
y: (a: string) => "2",
}
);