You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何指定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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 07:30:48