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

基于type属性扩展React组件Props的TypeScript类型问题

解决React输入组件基于type的Props类型校验问题

问题背景

我想要实现一个根据type值自动匹配对应Props的Input组件:

  • 当type="multiline"时,组件接收MultilineInputProps
  • 当type="icon"时,组件接收IconInputProps

原实现代码如下:

import React, { memo, useCallback, ReactNode } from 'react'
import Multiline from 'src/components/Input/variaties/Multiline'
import Icon from './variaties/Icon'
import Default from './variaties/Default'
import Select from './variaties/Select'

interface InputProps {
  type?: 'multiline' | 'icon'
  className?: string
}

interface MultilineInputProps {
  // placeholder: string // Omitting in this example for simplification
  className?: string
  height?: string
}

export interface IconInputProps {
  // placeholder: string
  className?: string
  iconRight?: ReactNode
  iconLeft?: ReactNode
}

type InputComponentProps<T extends 'multiline' | 'icon'> = T extends 'multiline'
  ? MultilineInputProps
  : T extends 'icon'
    ? IconInputProps
    : never

const Input = ({
  type,
  className,
}: InputProps) => {
  const inputComponent = useCallback(() => {
    switch (type) {
      case 'select':
        return (
          <Select options={[]} placeholder={'Icon'} className={className} />
        )
      case 'icon':
        return <Icon placeholder={'Icon'} className={className} />
      case 'multiline':
        return <Multiline placeholder={'Multiline'} className={className} />
      default:
        return <Default placeholder={'Default'} className={className} />
    }
  }, [])

  return inputComponent()
}

export default memo(Input)

tsconfig配置:

{
  "compilerOptions": {
    "target": "ES2017",
    "module": "ESNext",
    "lib": [
      "es6",
      "dom"
    ],
    "strict": true,
    "declaration": true,    
    "strictBindCallApply": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "strictFunctionTypes": true,
    "strictNullChecks": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "jsx": "react",
    "noImplicitThis": true,
    "allowJs": true,
    "noEmit": true,
    "baseUrl": ".",
  },
  "include": [
    "src",
  ],
  "exclude": [
    "node_modules",
    "**/*.spec.ts"
  ]
}

当前问题

使用<Input>组件并指定type时,组件无法接收对应专属Props;尝试过泛型方案,但无法拒绝与当前type不匹配的错误属性。

解决方案

采用**类型歧视(discriminated union)**方案,通过联合类型严格约束不同type对应的Props集合,同时确保TypeScript能自动拦截错误属性。修改后的完整代码:

import React, { memo, useCallback, ReactNode } from 'react'
import Multiline from 'src/components/Input/variaties/Multiline'
import Icon from './variaties/Icon'
import Default from './variaties/Default'
import Select from './variaties/Select'

// 基础公共Props
interface BaseInputProps {
  className?: string
}

// 各类型专属Props(包含type作为歧视字段)
interface MultilineInputProps extends BaseInputProps {
  type: 'multiline'
  height?: string
}

interface IconInputProps extends BaseInputProps {
  type: 'icon'
  iconRight?: ReactNode
  iconLeft?: ReactNode
}

interface SelectInputProps extends BaseInputProps {
  type: 'select'
  options: any[]
}

interface DefaultInputProps extends BaseInputProps {
  type?: never // 默认类型不指定type,或type为undefined
}

// 联合类型:整合所有可能的Input Props
type InputProps = MultilineInputProps | IconInputProps | SelectInputProps | DefaultInputProps

const Input = memo((props: InputProps) => {
  const inputComponent = useCallback(() => {
    switch (props.type) {
      case 'select':
        return <Select {...props} placeholder={'Icon'} />
      case 'icon':
        return <Icon {...props} placeholder={'Icon'} />
      case 'multiline':
        return <Multiline {...props} placeholder={'Multiline'} />
      default:
        return <Default {...props} placeholder={'Default'} />
    }
  }, [props])

  return inputComponent()
})

export default Input

方案优势

  • 利用type作为歧视字段,TypeScript能自动推断不同类型对应的属性集合,传入错误属性会直接触发类型报错
  • 组件内部通过switch语句,TypeScript会自动收窄props的类型,确保传递给子组件的Props类型完全匹配
  • 代码结构更清晰,各类型的Props边界明确

内容的提问来源于stack exchange,提问作者Nilton Schumacher F

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 20:39:49