说了多少遍要用interface定义组件属性类型

不满

"说了多少遍要用interface定义组件属性类型,你怎么还在使用type定义组件的属性类型。你自己加一下这个组件的showCount属性的定义,再发包给他。"

我对这个小伙子是越来越不满意了。已经宣贯多少遍了,要用interface定义组件的属性类型,还是使用 type 定义。

起因

部门在开发一个表单控件组件库 wform ,其中包含一个 WInput 组件。这个组件是基于 Ant Design 的 Input 组件进行的二次封装。想使用 Input 组件的 showCount 功能(显示已输入文本的长度)时,向 WInput 组件添加 showCount 属性后发现 TypeScript 报错。这是因为 wInput 组件的属性类型定义 WInputProps 中没有包含 showCount 属性的定义。

A部门的同事向我反馈了这个错误,我看到了,想了一下现在组件库更新版本的流程挺麻烦的,于是对他说:"这个是组件属性类型漏定义了,你可以先使用 declare module 语句来扩展这个组件属性类型。"

"使用 declare module 语句扩展,怎么弄,不太会。"

"好吧,这个方法的确不常用,很简单的,我来教你一下。"

打脸

首先,你需要创建一个.d.ts文件,根据库的名称来命名这个文件,比如 wform.d.ts 。但是要确保这个文件被放置在 tsconfig.jsoninclude 字段中指定的目录下。

然后,在wform.d.ts中写入以下代码,其中declare module的参数是一个模块路径,这里最好使用通配符*声明模块路径,避免模块路径错误。

ts 复制代码
declare module 'wform/*' {
  export interface WInputProps {
    showCount: boolean;
  }
}

最后,见证奇迹。

结果被打脸了。TypeScript 报错依旧存在。不应该啊,开始排查。

排查

打开 tsconfig.json 文件,查看 include 字段的值。

json 复制代码
{
  "include": [
    "./src",
  ]
}

在看 wform.d.ts 文件路径,没错啊,是在工程的src文件夹下。

会不会是引入wInput组件的模块路径不对,查看一下wInput组件引入的代码实现。

tsx 复制代码
import { WInput } from 'wform';

没错啊,WInput组件引入的模块路径是匹配declare module声明的模块路径。

再观察一下,TypeScript 报错,发现现在是 showCount 属性不报错,其它属性却提示不存在,比如原先的存在 value 属性现在提示不存在。

这应该是通过 declare module 定义的 WInputProps 属性类型,把原先在 WInput 组件中的定义的 WInputProps 属性类型给覆盖了。

罪魁祸首

马上查看了 WInput 组件的代码,发现 WInputProps 属性类型是用 type 来定义的。

ts 复制代码
export type WInputProps = {
  value?: string;
  // ...
}

对质

找到 WInput 组件的开发负责人,质问:"为啥不用interface 定义 WInputProps?"

"typeinterface都可以用定义对象类型,没啥区别,为啥不行呢。"

"好吧,原来他根本不懂typeinterface的区别,真不知道当初怎么把他招进来的。"

typeinterface接口的最大区别

使用type定义的类型称为类型别名,interface定义的类型称为接口。

同名类型别名会冲突,同名接口会自动合并。

ts 复制代码
type UserInfo = {
  name: string;
};
// 标识符"UserInfo"重复
type UserInfo = {
  age: number;
};

同名接口会自动合并

ts 复制代码
interface UserInfo {
  name: string;
}
interface UserInfo {
  age: number;
}

const userInfo: UserInfo = {  name: "张三", age: 23 };
userInfo.name; // "张三"
userInfo.age; // 23

在组件库中定义对象比如组件属性时候最好使用 interface,这样方便使用者可以利用 declare module 语句自由地扩展。

接口合并的基本规则

当然接口合并也是有一定规则的,在通常的场景只要考虑以下两点基本规则即可。

  1. 非函数的成员:如果同名接口包含非函数的成员,这些成员必须是唯一的。如果它们不唯一,那么它们必须是相同的类型。如果不同名的成员有相同的类型,则认为是有效的;如果类型不同,则会导致编译错误。

  2. 函数成员:对于函数成员,每个同名函数成员都会被视为这个函数的一个重载。TypeScript 会使用这些重载创建一个重载列表。在调用函数时,TypeScript 会尝试使用第一个重载,如果不匹配,就会继续尝试下一个,直到找到一个匹配的重载。

相关推荐
前端小巷子5 分钟前
JS打造“九宫格抽奖”
前端·javascript·面试
潘小安7 分钟前
『译』资深前端开发者如何看待React架构
前端·react.js·面试
李昊哲小课32 分钟前
HTML 完整教程与实践
前端·html
老华带你飞1 小时前
畅阅读小程序|畅阅读系统|基于java的畅阅读系统小程序设计与实现(源码+数据库+文档)
java·数据库·vue.js·spring boot·小程序·毕设·畅阅读系统小程序
GISer_Jing1 小时前
React 18的createRoot与render全面对比
前端·react.js·前端框架
我叫汪枫1 小时前
React Hooks原理深度解析与高级应用模式
前端·react.js·前端框架
我叫汪枫1 小时前
深入探索React渲染原理与性能优化策略
前端·react.js·性能优化
阿智@111 小时前
推荐使用 pnpm 而不是 npm
前端·arcgis·npm
伍哥的传说1 小时前
QRCode React 完全指南:现代化二维码生成解决方案
前端·javascript·react.js·qrcode.react·react二维码生成·qrcodesvg·qrcodecanvas
IT_陈寒1 小时前
Vite 5.0 终极优化指南:7个配置技巧让你的构建速度提升200%
前端·人工智能·后端