I'm learning functional programming and trying to implement assoc function (functional setter) as a higher order function with the support of nested properties. I'm okay with the depth being limited by the number of function overloads.
What I'm having now.
The actual implementation:
export type KeyOf<T> = T extends object ? keyof T : never;
export type Setter<S, A> = (whole: S) => (part: A) => S;
export function assoc<T, K1 extends KeyOf<T> = KeyOf<T>>(
k1: K1
): Setter<T, T[K1]>;
export function assoc<
T,
K1 extends KeyOf<T> = KeyOf<T>,
K2 extends KeyOf<T[K1]> = KeyOf<T[K1]>
>(k1: K1, k2: K2): Setter<T, T[K1][K2]>;
export function assoc<
T,
K1 extends KeyOf<T> = KeyOf<T>,
K2 extends KeyOf<T[K1]> = KeyOf<T[K1]>,
K3 extends KeyOf<T[K1][K2]> = KeyOf<T[K1][K2]>
>(k1: K1, k2: K2, k3: K3): Setter<T, T[K1][K2][K3]>;
export function assoc<
T,
K1 extends KeyOf<T> = KeyOf<T>,
K2 extends KeyOf<T[K1]> = KeyOf<T[K1]>,
K3 extends KeyOf<T[K1][K2]> = KeyOf<T[K1][K2]>,
K4 extends KeyOf<T[K1][K2][K3]> = KeyOf<T[K1][K2][K3]>
>(k1: K1, k2: K2, k3: K3, k4: K4): Setter<T, T[K1][K2][K3][K4]>;
export function assoc(...path: any[]): any {
return (whole: any) => {
return (value: any) => {
return assocDeep(whole, path as any, value);
};
};
}
But when I use it with three keys (or more), it seems it can't resolve keys deeper than the second level:
import { assoc } from './assoc';
type Company = {
readonly id: number;
readonly name: string;
readonly address: Address;
};
type Address = {
readonly country: string;
readonly region: string;
readonly street: Street;
readonly building: string;
};
type Street = {
readonly name: string;
readonly kind: string;
};
const setStreetName = assoc<Company>('address', 'street', 'name');
~~~~~~
As result the TypeScript compiler says:
Argument of type 'string' is not assignable to parameter of type 'never'. ts(2345)
How can I make it work for more levels of nesting?