1. 为啥这么干?
- 不想用 TypeScript(或是老旧项目,暂时无法改造成 TypeScript 项目)
- 只想利用编辑器对 TypeScript 类型定义的自动提示
- 需要对 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. 完了
虎年快了,拜拜。