我有一个带有methods
参数的组件,它的类型定义来自一个导入的库。
import { useForm } from "react-hook-form";
interface FormParams {
commonField: string
optionalField1: string
optionalField2: number
}
function App() {
const methods = useForm<FormParams >();
const { register, handleSubmit, watch } = methods
useCustomHook({ methods, optionalFieldName: 'optionalField2' })
}
在这个组件中,我调用了一个函数,该函数需要这个methods
参数和一个可选表单参数的名称作为参数。
这是这个函数的实现
import { FieldPath, FieldValues, useForm, UseFormReturn } from "react-hook-form";
type CustomFieldValues = FieldValues & {
commonField: string
}
type HookParams = {
methods: UseFormReturn<CustomFieldValues>
optionalFieldName?: FieldPath<CustomFieldValues>
}
const useCustomHook = ({ methods, optionalFieldName = 'optionalField1' }: HookParams) => {
const { watch } = methods
const fieldValue = watch(optionalFieldName) // ❌ returns with type `any` because of wrong typing
const commonFieldValue = watch('commonField') // ✅ returns with correct `string` type
// do some stuff
}
函数的类型不完整。在此处,watch
函数返回给定窗体参数名称的当前值。当前,commonFieldValue
的类型正确地为string
,但fieldValue
的类型正确地为any
。
如果我可以用UseFormReturn<FormParams>
来表示methods
,我就不会有这个问题了,但是这个函数实际上是从很多有不同表单参数的组件中调用的,唯一一个公共的参数是commonField
,而其他字段是基于传递给函数的参数。这段代码可能看起来毫无用处,但我试图尽可能地简化它,以专注于问题。
所以如果我用
useCustomHook({ methods, optionalFieldName: 'optionalField2' })
此fieldValue
参数应键入为number
。
const fieldValue = watch(optionalFieldName)
如果我用调用函数
useCustomHook({ methods })
此fieldValue
参数应键入为string
(因为默认值为optionalField1
)。
const fieldValue = watch(optionalFieldName)
我们可以为HookParams定义一个泛型类型吗?这样我就可以在函数内部得到正确的类型定义。
下面是针对该问题的sandbox。
1条答案
按热度按时间3qpi33ja1#
你不可能做到100%类型安全,
UseFormReturn
的方式是不变的(所以UseFormReturn<CustomFieldValues>
和UseFormReturn<FormParams>
之间没有继承关系,请看我关于变量here的讨论)然而,当涉及到重载时,你可以使用更宽松的可赋值性检查。因此,我们将有一个公共函数签名,允许我们传入派生类型和一个实现签名,将使用
UseFormReturn<CustomFieldValues>
。现在我想强调的是,这将不是100%类型安全的,你可能能够做你应该被允许的事情,但这是它将得到的最好结果Playground链接
注:在实现签名上我还必须添加
Record<string, unknown>
。这是为了允许我们用一个我们不知道的任意属性进行索引。这意味着你可以用任何字符串调用watch
,但是你会得到unknown
,如果你用一个已知属性调用watch,你会得到正确的类型。