首页 > 解决方案 > 未捕获打字稿函数返回类型

问题描述

我在这里缺少什么, 我认为它应该是result类型{foo: unknown, bar: unknown, baz: unknown}{foo: number, bar: boolean, baz: string}

export function apply<A, B extends {[P in keyof A]: B[P]}>(
  functions: {[P in keyof A]: (a: A[P]) => B[P]},
  data: {[P in keyof A]: A[P]},
): {[P in keyof A]: B[P]} {
  const result = {} as {[P in keyof A]: B[P]}
  const keys = Object.keys(functions) as (keyof A)[]
  for(const key of keys)
    result[key] = functions[key](data[key])
  return result
}

const functions = {
  foo: (a: string) => a.length,
  bar: (a: number) => a === 42,
  baz: (a: boolean) => a ? 'true' : 'false'
}
const data = {foo: 'foo', bar: 42, baz: true}
const result = apply(functions, data)

标签: typescript

解决方案


首先让我们检查一下当前尝试的问题,即您为两个参数提供的类型。

functions: {[P in keyof A]: (a: A[P]) => B[P]},
data: {[P in keyof A]: A[P]}

看起来您希望该类型A[P]表示A对象的值类型。这里的问题是对 type 没有限制A,所以我们没有什么可以用来推断值的更具体的类型。因此,它们被推断为具有最一般的类型:unknown.

考虑到您可以在调用apply函数时手动指定泛型参数。就像是:

type A = {} // ??? what to put here?
type B = {} // ??? what to put here?
const result = apply<A, B>(functions, data)

问题是我不确定A在这个例子中我会给出什么类型。A事实上,除了你所看到的之外,我不确定这个构造中是否存在有效的类型{foo: unknown, bar: unknown, baz: unknown}


因此,与其对上述参数使用映射类型,为什么不将这些限制添加到泛型签名本身呢?看起来您希望functions参数是一个包含单个参数函数的对象。让我们限制泛型A以立即满足该条件:

A extends Record<string, (a: any) => any>

而对于B,这是您的数据类型,它应该与 中每个相应函数的参数类型匹配A。我们也可以在通用签名中应用该限制:

B extends {[P in keyof A]: Parameters<A[P]>[0]}

最后,对于返回类型,这是另一个对象,但不是对应于 的参数类型A,而是返回类型:

{[P in keyof A]: ReturnType<A[P]>

把所有这些放在一起:

function apply<A extends Record<string, (a: any) => any>, B extends {[P in keyof A]: Parameters<A[P]>[0]}>(
    functions: A,
    data: B
): {[P in keyof A]: ReturnType<A[P]> {
    const result = {} as {[P in keyof A]: B[P]}
    const keys = Object.keys(functions) as (keyof A)[]
    for(const key of keys)
        result[key] = functions[key](data[key])
    return result
}

const functions = {
    foo: (a: string) => a.length,
    bar: (a: number) => a === 42,
    baz: (a: boolean) => a ? 'true' : 'false'
}
const data = {foo: 'foo', bar: 42, baz: true}

const result = apply(functions, data)
// The type of `result` is inferred to be:
// {
//    foo: number;
//    bar: boolean;
//    baz: "true" | "false";
// }

推荐阅读