网站搜索

reactjs - 当一个带有错误 props 的 React 函数组件(React.FC)作为 prop 传递时,TypeScript 为什么不报错?


我正在尝试将一个带有一些 props 的函数组件发送到另一个组件,并在接收组件中尝试对该组件的一些 props 进行类型检查。以下是代码:

// BaseButton.tsx

export type ButtonProps = {
  label: string
  size?: 'sm' | 'md' | 'lg' | 'normal'
  icon?: React.FC<{ size?: number; foo: string }>
} & React.ButtonHTMLAttributes<HTMLButtonElement

export const BaseButton: React.FC<ButtonProps> = props => {/* ...return the button component */}
// Icon.tsx

type Props = {
  style?: React.CSSProperties
  size?: number
}

export const Icon: React.FC<Props> = ({ style, size = 24 }) => {/* ... return Icon component */}
// where I render the component

<BaseButton icon={Icon} label="Sign in" />
//                ^^^ I am expecting Typescript to complain

由于 ButtonProps 中的 icon?: React.FC<{ size?: number; foo: string }> 有一个额外的属性 foo,我本以为 TypeScript 会对此发出警告。但事实并非如此。

编辑:

在尝试用 JSX 语法调用它时,这将抛出一个错误:

icon?: React.FC<{ size?: number; foo: string }>
const {icon: Icon} = props
<Icon />

相反,类型应该是:

icon?: React.ComponentType<{ bar: number }>

答案#1

基本上,你可以将参数较少的函数分配给参数较多的函数:

let fn1 = (props: { size: number }) => 42
let fn2 = (props: { size: number; foo: string }) => 42

fn2 = fn1  // this works
fn1 = fn2 // error

或者换句话说:

对于一个变量 let fn2,其类型为 (props: { size: number; foo: string }) => number,客户端需要使用两个属性 size 和 foo 来调用它。现在我们可以将 fn2=fn1,因为底层实现 fn1 可以忽略某些输入属性,只处理 size。

转到您的案件:

具有类型 React.FC<{ style?: React.CSSProperties; size?: number}> 的 Icon 可以赋值给具有类型 React.FC<{ size?: number; foo: string }> 的 ButtonProps 的 icon 属性。

let fc1: React.FC<{
  style?: React.CSSProperties
  size?: number
}> = // ...
let fc2: React.FC<{ size?: number; foo: string }> = // ...

fc2 = fc1 // works
fc1 = fc2 // error

操场

答案#2

类型 FunctionComponent 有函数类型定义,因此你可以传递一个 React 组件或一个将来要调用的函数。

type FC<P = {}> = FunctionComponent<P>;

interface FunctionComponent<P = {}> {
    (props: PropsWithChildren<P>, context?: any): ReactElement | null; <--- Here
    propTypes?: WeakValidationMap<P>;
    contextTypes?: ValidationMap<any>;
    defaultProps?: Partial<P>;
    displayName?: string;
}

这是一个沙盒,展示了如何从 BaseButton 组件中调用 props.icon 组件。你将看到在第 21 行调用该函数时,必须传递 props foo 和 size。

https://codesandbox.io/s/trusting-banach-prwos?file=/src/App.tsx

https://www.typescriptlang.org/docs/handbook/interfaces.html#function-types