基于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
相关产品推荐
相关产品推荐

