我正在用TypeScript和Material UI (MUI)制作一个React组件。
它是一个呈现输入或文本的组件。
你使用道具mode
在它们之间切换,
如果你设置mode
为view
,它将渲染文本。
如果你将mode
prop设置为edit
,它将渲染输入。
代码如下:
import { Typography, TextField } from '@mui/material'
import type { TypographyProps, TextFieldProps } from '@mui/material'
interface InputComponentProps {
mode: 'edit' | 'view'
content?: string
}
type EditModeProps = TextFieldProps & {
mode: 'edit'
} & InputComponentProps
interface ViewModeProps extends TypographyProps, InputComponentProps {
mode: 'view'
}
export default function InputComponent(props: EditModeProps | ViewModeProps) {
const { mode, content, ...compProps } = props
if (mode === 'edit') return <TextField value={content} {...(compProps as EditModeProps)} />
if (mode === 'view') return <Typography children={content} {...(compProps as ViewModeProps)} />
}
TypeScript没有抱怨。但是,现在当我使用这个组件时,我在外部遇到了问题。
我通常可以这样使用,没有问题:
<InputComponent mode="edit" />
<InputComponent mode="view" />
但是,我不能这样做:
import InputComponent from './InputComponent'
interface UserProfilePageProps {
mode: 'edit' | 'view'
}
export default function UserProfilePage(props: UserProfilePageProps) {
const { mode } = props
// the following works
/* return <InputComponent mode={'view'} content="hello world" /> */
/* return <InputComponent mode={'edit'} content="hello world" /> */
// the following works but its showing a compile time error?
return <InputComponent mode={mode} content="hello world" />
}
现在TypeScript说:
Type '{ mode: "edit" | "view"; }' is not assignable to type 'IntrinsicAttributes & (EditModeProps | ViewModeProps)'.
Type '{ mode: "edit" | "view"; }' is not assignable to type 'ViewModeProps'.
Types of property 'mode' are incompatible.
Type '"edit" | "view"' is not assignable to type '"view"'.
Type '"edit"' is not assignable to type '"view"'.
CodeSandbox
TypeScript试图将你传递给InputComponent
的道具匹配到EditProps
和ViewProps
,并且由于UserProfilePageProps
的mode
可以是edit
或view
,因此存在歧义。这就是联合类型的工作方式。
你必须通过显式设置mode="view"
或mode="edit"
来告诉TypeScript这些道具属于联合的哪一部分。另一种方法可以是命令完全跳过匹配,即<InputComponent {...({ mode, content: 'hello world' } as Parameters<typeof InputComponent>[0])} />
。