I'm trying to figure out how to properly type the return value when an object with optional formatter functions is involved.
For a function with a single value it is working fine.
type Params = {
id?: number
created?: string
}
type FormatFn<TParam extends keyof Params, TValue> = (data: Params[TParam]) => TValue
type Result<TValue> = {
param: TValue
}
declare function query<TParam extends keyof Params, TValue = Params[TParam]>(
param: TParam,
formatter?: FormatFn<TParam, TValue>
): Result<TValue>;
// Works as expected 'created' is type 'Date'
const { param: created } = query('created', (created) => new Date(created || ''))
I want to add a version where you can input an array of strings and an object of optional formatter functions.
Here is a Playground with my attempt.
This would be my expected output:
queries(['id', 'created'], {
created: (created) => new Date(created || '')
})
// Instead of return type
// {
// params: {
// id: number,
// created: string
// }
// }
//
// I want to have this, because of the formatter function
// {
// params: {
// id: number,
// created: Date
// }
// }
type Params = {
id?: number
created?: string
}
type Elem = keyof Params;
type Fn = (value: any) => any
type Predicate<T extends Elem> = Record<T, (value: Required<Params>[T]) => any>
type Reducer<
Arr extends ReadonlyArray<Elem>,
Result extends Record<string, any> = {}
> = Arr extends []
? Result
: Arr extends readonly [infer H, ...infer Tail]
? Tail extends ReadonlyArray<Elem>
? H extends Elem
? Reducer<Tail, Result & Predicate<H>>
: never
: never
: never;
/**
* Pure js/ts analogy
*/
const reducerPredicate = (elem: string) => ({ [elem]: () => null });
const reducer = <Arr extends string[]>(
arr: Arr,
result: { [prop: string]: string } = {}
) => {
if (arr.length === 0) {
return result; // 1. end of recursion, this is the last call
}
const [head, ...tail] = arr; // 2. from first and before the last
return reducer(tail, { ...result, ...reducerPredicate(head) });
// no need for never branch
};
type Result<
P extends ReadonlyArray<keyof Params>,
Defaults extends Partial<Record<keyof Params, Fn>>,
Cache extends P[number] & keyof Defaults = P[number] & keyof Defaults> =
& { [Prop in Exclude<P[number], Cache>]: Required<Params>[Prop] }
& { [Prop in keyof Defaults]: Defaults[Prop] extends Fn ? ReturnType<Defaults[Prop]> : never }
function queries<TParams extends Array<keyof Params>, TValues extends Partial<Reducer<TParams>>>(
params: [...TParams],
formatter?: TValues
): Result<TParams, TValues> {
return null as any
}
const multiple = queries(['id', 'created'], {
created: (created /** string */) => 42,
id: (value /** number */) => 42
})
Here , in my blog, you can find more explanation.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.