首页 > 解决方案 > 在打字稿中定义 lambda 函数的问题

问题描述

我只是坚持使用打字稿中的以下 lambda 函数定义。这可能是一个新手问题。

type P = { id: string }
type F = <T>(x: T) => string
const f: F = (x: P) => x.id
f({ id: 'abc' }

但它一直在抱怨以下内容:

类型 '(x: P) => string' 不可分配给类型 'F'。

参数“x”和“x”的类型不兼容。

类型“T”不能分配给类型“P”。

更新#1

我只是评论了为什么我需要一个通用函数的上下文。我正在执行一个函数来验证输入对象,并且该对象的每个路径都可以配置以进行验证,如下所示:

type Option = { path: string; isVerified: <T>(t: T) => boolean }

const verify = (obj: any, options: Option[]): boolean => {
  const results = options.map(option => {
    const { path, isVerified } = option
    return isVerified(obj[path])
  })
  return results.every(x => x)
}

type A = number
type B = string
const obj = { a: 1 as A, b: 'abc' as B }

verify(obj, [
  { path: 'a', isVerified: (x: A): boolean => x > 0 },
  { path: 'b', isVerified: (x: B): boolean => x.startsWith('a') }
])

更新#2

感谢第一个答案,它解决了中列出的问题Update #1。然而,Update #1实际上是一个简化的问题。在实际情况下,pathunderOption类型可能是stringor RegExg,这使得响应的代码Paths[K]无效,如下所示:

type Option = { path: string | RegExp; isVerified: <T>(t: T) => boolean }

const verify = (obj: any, options: Option[]): boolean => {
  const results = Object.keys(obj).map(k => {
    const verifiers = options
      .filter(opt =>
        typeof opt.path === 'string' ? opt.path === k : opt.path.test(k)
      )
      .map(x => x.isVerified)
    return verifiers.every(verify => verify(obj[k]))
  })

  return results.every(x => x)
}

type A = number
type B = string
const obj = { a: 1 as A, b: 'abc' as B }

verify(obj, [
  { path: 'a', isVerified: (x: A): boolean => x > 0 },
  { path: /b/, isVerified: (x: B): boolean => x.startsWith('a') }
])

有关更多详细信息,请参阅Playground 链接

它一直在下面抱怨:

类型 '(x: number) => boolean' 不可分配给类型 '(t: T) => boolean'。

参数“x”和“t”的类型不兼容。

类型“T”不能分配给类型“数字”。


标签: typescript

解决方案


关于示例代码:

type P = { id: string }
type F = <T>(x: T) => string
const f: F = (x: P) => x.id

该类型F是从任何类型T到的泛型函数string;所以本质上,对于一个函数来说,F它必须接受任何类型的参数。您的函数f只接受 type 的参数P,因此该函数不是 type F


关于您要解决的实际问题,这里真正的问题是Option应该是联合类型,因为参数类型isVerified取决于 的值path

type Option = {
    path: 'a',
    isVerified: (t: number) => boolean
} | {
    path: 'b',
    isVerified: (t: string) => boolean
}

为了使这个联合类型更容易为实际用例(可能有更多字段)声明,Paths为您正在验证的对象定义一个类型,然后构造Option为映射类型:

type Paths = { a: number, b: string }

type Option = {
  [K in keyof Paths]: {
    path: K,
    isVerified: (t: Paths[K]) => boolean
  }
}[keyof Paths]

现在您的函数调用verify将进行类型检查,甚至会为您推断参数类型,而无需您编写显式注释:

const obj = { a: 1, b: 'abc' }

// OK
verify(obj, [
  { path: 'a', isVerified: x => x > 0 },
  { path: 'b', isVerified: x => x.startsWith('a') }
])

此外,如果参数类型错误,您将收到类型错误(根据需要)isVerified,因为path属性缩小了isVerified应具有的类型:

// type error
verify(obj, [
  { path: 'a', isVerified: x => x.startsWith('a') },
  { path: 'b', isVerified: x => x > 0 }
])

您还需要进行另一项编辑 -verify函数中的类型断言,因为否则isVerified会被推断为采用类型参数never(即交集 number & string):

const verify = (obj: any, options: Option[]): boolean => {
  const results = options.map(option => {
    const { path, isVerified } = option

    // this line needs a type assertion
    return (isVerified as (t: Paths[typeof path]) => boolean)(obj[path])
  })
  return results.every(x => x)
}

游乐场链接


推荐阅读