typescript - 如何使用格式化程序函数的可选对象键入函数返回类型

标签 typescript typescript-generics

我正在尝试弄清楚当涉及具有可选格式化程序函数的对象时如何正确键入返回值。

对于具有单个值的函数,它工作正常。

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 || ''))

我想添加一个版本,您可以在其中输入字符串数组和可选格式化程序函数的对象。

这是一个Playground我的尝试。

这将是我的预期输出:

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
})

Playground

Here ,在我的博客中,您可以找到更多解释。

关于typescript - 如何使用格式化程序函数的可选对象键入函数返回类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68359417/

相关文章:

typescript - 我可以根据参数更改返回类型吗

typescript - 如何输入以接口(interface)作为参数的方法

typescript - 类型 'string' 不能用于索引类型 'unknown ||或者如何让 typescript 分配 Type[key] = SameType[key]

javascript - 将类型定义添加到将 props 注入(inject)组件的 HOC

oracle - 如何将此 oracle 查询转换为 typeorm?

typescript - 为什么 typescript 认为我的属性(property)在我投入 promise 捕获 block 后可能无效?

javascript - 类型检查和泛型

typescript - 如何将属性函数的返回类型推断为属性的类型?

typescript - 如何在 typescript 中使用 "fill in"泛型参数

javascript - 如何将这个 Angular 工厂转换为 typescript ?