为何指定toValue时inputProps类型为any?TypeScript类型问题修复
问题
我有一个包含toValue、fromValue、children三个props的组件:前两个为可选属性,但需同时指定或同时省略;children是接收inputProps参数的函数,inputProps类型应为toValue的返回类型TInputValue(若指定toValue)或TStateValue。
我编写了如下代码,但指定toValue时inputProps的类型为any,该如何修复?
interface IP<T> { value: T onChange: (value: T) => void } interface A<TStateValue> { toValue?: never fromValue?: never children: (inputProps: IP<TStateValue>) => null } interface B<TStateValue, TInputValue> { toValue: (value: TStateValue) => TInputValue fromValue: (value: TInputValue) => TStateValue children: (inputProps: IP<TInputValue>) => null } type C<TStateValue, TInputValue> = | A<TStateValue> | B<TStateValue, TInputValue> const propsWithoutToValue: C<number, string> = { children: (inputProps) => null, // `inputProps` 类型为 `IP<number>`,正常! } const propsWithToValue: C<number, string> = { toValue: (value) => value.toString(), // 返回类型为 `string`,所以 `inputProps` 应该是 `IP<string>` fromValue: (value) => Number(value), children: (inputProps) => null, // `inputProps` 类型为 `any`,需要修复 }
解决方案
问题原因
当前使用联合类型C时,TypeScript在推断children函数的参数类型时,会取联合分支中两个children函数参数类型的交集:IP<number> & IP<string>。由于number和string的交集是never,TypeScript无法有效推断类型,最终退化为any。
修复方案
使用函数重载来精准区分两种props场景,让TypeScript能根据传入的toValue/fromValue自动推断inputProps的类型:
interface IP<T> { value: T onChange: (value: T) => void } // 保持联合类型的约束逻辑 type C<TStateValue, TInputValue = TStateValue> = | { toValue?: never fromValue?: never children: (inputProps: IP<TStateValue>) => null } | { toValue: (value: TStateValue) => TInputValue fromValue: (value: TInputValue) => TStateValue children: (inputProps: IP<TInputValue>) => null } // 用函数重载实现自动类型推断 function createComponentProps<TStateValue>( props: { toValue?: never fromValue?: never children: (inputProps: IP<TStateValue>) => null } ): C<TStateValue> function createComponentProps<TStateValue, TInputValue>( props: { toValue: (value: TStateValue) => TInputValue fromValue: (value: TInputValue) => TStateValue children: (inputProps: IP<TInputValue>) => null } ): C<TStateValue, TInputValue> function createComponentProps(props: any) { return props } // 使用示例 const propsWithoutToValue = createComponentProps<number>({ children: (inputProps) => null, // inputProps 自动推断为 IP<number> }) const propsWithToValue = createComponentProps<number>({ toValue: (value) => value.toString(), fromValue: (value) => Number(value), children: (inputProps) => null, // inputProps 自动推断为 IP<string> })
简化修复(无需函数)
如果不想引入函数,也可以手动指定children函数的参数类型,明确约束:
const propsWithToValue: C<number, string> = { toValue: (value) => value.toString(), fromValue: (value) => Number(value), children: (inputProps: IP<string>) => null, // 手动指定参数类型 }
内容的提问来源于stack exchange,提问作者Ilya Ordin
相关产品推荐
相关产品推荐

