使用 JSDoc 引入 React 组件库的 TypeScript 类型定义

1,169 阅读3分钟

1. 为啥这么干?

  1. 不想用 TypeScript(或是老旧项目,暂时无法改造成 TypeScript 项目)
  2. 只想利用编辑器对 TypeScript 类型定义的自动提示
  3. 需要对 React 组件库某个组件的属性进行增强拓展

2. 举个栗子

比如我们要对 Ant Design 组件库的 Input 组件进行增强,添加一个 theme 属性:

import { Input } from 'antd';

const ThemeInput = ({ theme, ...inputProps }) => {
  return (
    <div className={theme}>
      <Input {...inputProps} />
    </div>
  );
};

export default ThemeInput;

这时,我们希望对 ThemeInput 组件添加 JSDoc,从而获得编辑器自动提示。

ThemeInput 除了自定义的 theme 外,其它属性全部来自 antd Input 组件的已有属性。

这个 JSDoc 应该咋整呢?

3. 解决方案

经过我的努力摸索(这个真的是摸索,因为这玩意没有写明白的解决方案,写错也没有任何提示,只能 StackOverflow + GitHub 搜来搜去,然后不停的试,看哪种方案可以 work ...

经过我的努力摸索,努力努力摸索,非常努力的摸索 ... 好吧,直接放解决方案:

import { Input } from 'antd';

/**
 * @typedef {import('antd/es/input/Input').InputProps} InputProps
 */

/**
 * @typedef {Object} ThemeInputExtra
 * @property {string} theme
 */

/**
 * @param {InputProps & ThemeInputExtra} props
 */
const ThemeInput = (props) => {
  const { theme, ...inputProps } = props;

  return (
    <div className={theme}>
      <Input {...inputProps} />
    </div>
  );
};

export default ThemeInput;

这就完了,也不需要添加 jsconfig.json 什么的,就上面这样就挺好。

使用 import('') 语法引入 Input 组件的类型定义(参见:www.typescriptlang.org/docs/handbo…)。

再加上增强属性的类型定义,完工。

此时使用 ThemeInput 组件,就可以获得编辑器对 theme 属性以及 Input 原始属性的自动提示了~

目前就发现上面这种方案是可行的(当然,可能也有别的方案,但我还妹发现)。

在 WebStorm 和 VS Code 里都进行了测试,工作良好。

4. 害有啥呢?

TLDR,下面的如果赶时间就可以不用看了!

① 别的写法

也可以这么写 ↓:

/**
 * @typedef {Object} ThemeInputExtra
 * @property {string} theme
 */

/**
 * @param {import('antd/es/input/Input').InputProps & ThemeInputExtra} props
 */

害可以这么写 ↓:

/**
 * @param {import('antd/es/input/Input').InputProps & { theme: string }} props
 */

当然,还是推荐解决方案里的写法,更清晰一点,且可拓展。

比如又要加上 Textarea 组件的类型定义:

/**
 * @typedef {import('antd/es/input/Input').InputProps} InputProps
 * @typedef {import('antd/es/input/Textarea').TextAreaProps} TextareaProps
 */

/**
 * @typedef {Object} ThemeInputExtra
 * @property {string} theme
 */

/**
 * @param {InputProps & TextareaProps & ThemeInputExtra} props
 */

❌❌❌ 但是不能这么写 ↓ !!这样写就罢工了。

/**
 * @typedef {import('antd/es/input/Input').InputProps} InputProps
 * @typedef {Object} ThemeInputExtra
 * @property {string} theme
 * @param {InputProps & ThemeInputExtra} props
 */

② 增强属性

增强属性当然不是只能写一个 theme,可以无限增加:

/**
 * @typedef {Object} ThemeInputExtra
 * @property {string} theme
 * @property {number} props1
 * @property {boolean} props2
 * @property {Array} props3
 * ...
 */

③ 类型命名的小陷阱

理论上,类型定义可以随便命名,比如:

/**
 * @typedef {import('antd/es/input/Input').InputProps} Hahahaha
 */

/**
 * @typedef {Object} Lalalala
 */

/**
 * @param {Hahahaha & Lalalala} props
 */

但假如你命名成 Props

/**
 * @typedef {import('antd/es/input/Input').InputProps} Props
 */

/**
 * @param {Props & ThemeInputExtra} props
 */

就会发现又罢工了...

我猜应该是全局命名冲突,没有深入研究。

所以,最好让每个组件内「类型定义」的命名,都全局唯一。

类似上面的 ThemeInputExtra,命名格式为 XXXExtra,保持命名空间唯一性。

④ 玄学

当然,如果你怎么折腾都还是不行,可以试试重启编辑器之类的,因为这个 sometimes 是有点玄学的 ...

5. 完了

虎年快了,拜拜。