指定具有未知参数的函数数组会引发类型错误

Specifying array of functions with unknown arguments throws a typeerror

我设置了一个相当简单的钩子,叫做 useField.ts,它的定义如下:

type CheckFunction = <T>(input: T, ...args: unknown[]) => unknown

export const useField = <T>(
   input: T,
   ...checkFunctions: CheckFunction[]
) => {
  // .... and the code goes here, but irrelevant for the question
} 

之后,我开始在 useField.test.tsx 中按以下方式编写测试:

import { renderHook } from '@testing-library/react-hooks'
import { useField } from '../useField'

describe('useField error functions', () => {
 
  const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/gi
  const isValidEmail = (input: string): boolean => !!input.match(emailRegex)

  test('Basic checkFunction', () => {
     const { result } = renderHook(() => useField('valid@email.com', isValidEmail)
     // ^ I get a type-error here
  })
})

我尝试传递 isValidEmail 函数的行有以下 错误:

Argument of type '(input: string) => boolean' is not assignable to parameter of type 'CheckFunction'.
  Types of parameters 'input' and 'value' are incompatible.
    Type 'T' is not assignable to type 'string'.

现在这可以通过将 checkFunctions 的定义分配给 any 从技术上解决,但这是我想提防的事情,因为它有点违背了类型检查的目的。

我也想过用泛型,但问题是,参数可以有多种类型,不能用一个泛型的数组来表达

知道我做错了什么吗?因为从逻辑的角度来看,unknown 的用法在这里似乎是正确的。

假设你的检查函数只接受一个参数,为什么你将它定义为接受多个值?例如。 ...args: unknown[]。您还可以指定它 returns 一个 boolean 而不是 unknown.

你可以做这样的事情,它实际上使用了一个泛型,但据我所知似乎很适合你的用例:

export const useField = <T>(
    input: T,
    ...checkFunctions: Array<(value: T) => boolean>
) => {
    // .... and the code goes here, but irrelevant for the question
}

const result = useField('someString', validator1, validator2);
// ^ It would know T is a string here, since the input is a string
// Therefore it'll check that `validator1` is of the type `(value: string) => boolean`
const result = useField('someString', str => {
    // TypeScript knows the type of `str` is string
    return str.includes('something');
});
// This would error:
const result = useField<string>(123, validator1);
// As well as this due to the validator accepting the wrong value:
const result = useField('string', (value: number) => true);

这是假设 useField 需要实际输入的值,而不是 <input> 元素的 id/name。否则没有理由让 input 成为通用的(毕竟 name/id 总是一个字符串)并且你不会命名 <input> valid@email.comemail 相反。


问题中的更新代码。

您对“通用”CheckFunction 类型的定义不正确。改用这个:

type CheckFunction<T> = (input: T, ...args: unknown[]) => unknown

export const useField = <T>(
    input: T,
    ...checkFunctions: CheckFunction<T>[]
) => {
    // .... and the code goes here, but irrelevant for the question
}

您使用 type Type = <T>(... 的版本基本上告诉 TypeScript“此函数具有 a 泛型,但在 Type 之外无关紧要”。如果您使用 type Type<T>,则您告诉 TypeScript 该类型很重要,例如应该由类型的用户指定。

简而言之,通过使用 type Type<T>,您可以让 useField 指定 Type 中的 T 代表什么,例如与 useFieldT.

类型相同