Conditional Props in React

TypeScript

在 React 中,经常会有这样的场景:通过某一个参数是否是真值,来决定某一个元素是否需要显示出来。

以 Ant Design 为例,Tooltip 的定义中,就包含了 title 这个参数,用于决定是否显示 Tooltip 及显示什么。如果传递的是 falsenull 或者 undefined,那么最终 Tooltip 就不会被显示出来。

常用的调用形式可能如下:

<Tooltip title={!this.state.hide && 'text'} />

在最初 Ant Design 对此的定义上,使用了如下的 TypeScript 类型定义:

interface Props {
  // ...
  title?: React.ReactNode | RenderFunction;
  // ...
}

这里,title 的定义用到了“可选参数”。看上去,是符合预期的行为,然而这里有几个细节值得注意:

  1. React.ReactNode 的定义是:
type ReactNode =
  ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;

可以看到,即使不是可选参数,undefined 等一系列值也是可以赋予给 title 的;

  1. title?: stringtitle: string | undefined 之间存在着细微的差别。

这里展开对比一下 title?: stringtitle: string | undefined 之间的细微差别。如果定义的类型是 title?: string,那么,以下的调用方式都是正确的:

  1. 传递字符串作为参数:
<Tooltip title="string" />
  1. 传递 undefined 作为参数:
<Tooltip title={undefined} />
  1. 不传递参数:
<Tooltip />

而如果是 title: string | undefined,那么上面的第三种方案(即不传参数)就是不可行的。

还是以 Tooltip 为例,显然前两种调用方法都是真实存在的场景,毕竟 Tooltip 可能是需要根据外部条件来选择性展示的;但是对于第三种场景,即不提供 title 数据、一直保持不渲染 Tooltip 的状态,可以认为是有错误的,应该由 TypeScript 进行检查并报错。

故,改成以下这种形式就可以了,毕竟 React.ReactNode 就允许了 undefined 的使用:

interface Props {
  // ...
  title: React.ReactNode | RenderFunction;
  // ...
}

Ant Design 对这种情况进行了修正