在react组件上使用typescript重载函数?



取一个简单的react组件

interface MyProps {
color: string
name?: string
height?: number
isBoy?: boolean
// only add the following if isBoy is true
actionHero: string
nickname: string
}
function MyComponent(props: MyProps){
...
}

可以看到,如果isBoy设置为true,则目标是需要actionHeronickName。否则,它们不会被使用。

我认为这是通过函数重载在typescript,但你怎么做它在反应?

在这种情况下,您不需要重载组件。考虑这个例子:

import React from 'react'
type Boy = {
color: string
name?: string
height?: number
isBoy: false
}
type ExtendedBoy = Omit<Boy, 'isBoy'> & {
actionHero: string
nickname: string
isBoy: true;
}
type Props = Boy | ExtendedBoy;
function MyComponent(props: Props) {
if (props.isBoy) {
props.nickname // stirng
} else {
props.isBoy // false
}
return <div></div>
}
const withBoy = <MyComponent isBoy color="red" actionHero={'batman'} nickname={'qwert'} /> // ok
const withoutBoy = <MyComponent isBoy={false} color="red" /> // ok

游乐场

我使用了区分联合而不是函数重载。

,如果你仍然想重载你的组件,没有人能阻止你:

import React, { FC } from 'react'
type Boy = {
color: string
name?: string
height?: number
isBoy: false
}
type ExtendedBoy = Omit<Boy, 'isBoy'> & {
actionHero: string
nickname: string
isBoy: true;
}
const MyComponent: FC<Boy> & FC<ExtendedBoy> = (props) => {
if (props.isBoy) {
props.nickname // stirng
} else {
props.isBoy // false
}
return <div></div>
}
const withBoy = <MyComponent isBoy color="red" actionHero={'batman'} nickname={'qwert'} /> // ok
const withoutBoy = <MyComponent isBoy={false} color="red" /> // ok

游乐场

请注意函数的交叉会产生函数重载FC<Boy> & FC<ExtendedBoy>

如果您对react组件中的typescript验证感兴趣,请参阅我的文章和博客。


@NicoRichter问了一个很好的问题:为什么

React.createElement(MyComponent, { isBoy: false, color: 'red' });

失败吗?

在回答这个问题之前,请尝试将超载的FC<Boy> & FC<ExtendedBoy>更改为FC<ExtendedBoy> & FC<Boy>。这听起来可能很奇怪,但它确实是一个"游戏规则改变者",因为我们正在处理重载。当createElements从重载的函数中推断出一个props时,它只是抓取上一次重载(根据设计)。这就是为什么它没有按我们期望的方式工作。

的例子:

function foo(a: string): number
function foo(a: number): string
function foo(a: number | string) {
return null as any
}
const fn = <Fn extends (...args: any[]) => any>(fn: Fn,...props:Parameters<Fn>) => {
}
const result = fn(foo,2) // ok, expects number

这是可能的,因为类型没有条件逻辑,不能相互依赖,只有两个映射类型。一个可选属性,另一个用于必需的。

interface MyProps {
color: string
name?: string
height?: number
isBoy?: false | null
}
interface MyProperRequired extends MyProps {
isBoy: true
// only add the following if isBoy is true
actionHero: string
nickname: string
}
function MyComponent(props: MyProps | MyProperRequired) {
}

MyComponent({ color: "red", height: 1, name: "hi", isBoy: true })