稀土掘金技术社区 2024年11月10日
为什么在TypeScript上不使用interface/type来声明业务数据结构
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文探讨了在TypeScript前端开发中,为什么不建议使用interface来声明业务数据结构。作者列举了三个主要原因:属性名称和类型的突变导致代码维护困难,无法为声明的业务数据扩展行为,以及面向对象编程的优势。文章强调了使用class类来声明数据结构的优势,包括可以标记装饰器、行为和属性共存、使用抽象类和抽象属性等,并指出编程范式只是工具,前端结合函数式和面向对象编程可以更有效率。最后,作者还分享了相关代码仓库,欢迎读者进一步了解和学习。

🤔属性名称的突变:后端接口字段名称发生变化,前端代码需要大量修改,导致维护困难,例如将用户姓名字段从name改为realName。

🔄属性类型的突变:后端接口字段类型发生变化,前端代码需要修改,例如用户状态从布尔值改为数字类型,导致代码逻辑需要重新调整。

💡无法为声明的业务数据扩展行为:interface只能声明数据结构,无法扩展对象的方法,例如无法直接为用户对象添加eat方法,而class类可以实现行为和属性的共存。

🧰class类的优势:class类支持标记装饰器,可以实现行为和属性共存,支持抽象类和抽象属性,更有利于代码组织和扩展。

🤔编程范式:作者认为编程范式只是工具,前端可以结合函数式和面向对象编程,提高代码效率。

原创 Hamm 2024-11-10 09:01 重庆

点击关注公众号,“技术干货”及时达!

点击关注公众号,“技术干货” 及时达!

一、写在前面

之前我写了一篇 《也许跟大家不太一样,我是这么用TypeScript来写前端的》 有提到说 “我们不允许在TypeScript中使用 interface type 来声明业务数据结构。” 很多朋友评论想知道原因,那么我们今天来聊聊为什么。

当然,我们必须得强调一下,只是业务数据结构不允许。而如果是第三方库的数据结构,或者是第三方服务对接的数据结构,或者是装饰器的参数配置,组件的参数配置等,我们还是睁一只眼闭一只眼的,但依然不推荐。

1. 属性名称突入起来的转变

我们声明了 客户用户 管理员用户 部门用户 如下:

// 客户用户
interface CustomerUser {
    account: string
    name: string
    age: number
    // 更多属性}
// 部门
interface Department{
    code: string
    name: string
    // 更多属性}
// 超管
interface AdminUser {
    account: string
    name: string
    // 更多属性}
// 其他使用的地方
<span>客户姓名:{{customUser.name}}</span>
<span>制单部门:{{department.name}}</
span>
<span>审批人员:{{adminUser.name}}</span>

这是前期与后端的同学约定的数据结构,前端按照这个结构已经开发完成了,等待与后端联调。

此时后端的主管扭扭捏捏的走过来,扯着自己的衣角对你说:“朋友,那个,用户的姓名字段我们想改一下,标准一点叫 realName, 你们配合改一下呗~”

你大腿一拍,虽然很生气,但为了不丢面子,说,“没问题,小事情,包在我身上。”

可是你搜索了一下 .name 的地方,有88个文件。你缩小了下搜索范围到 User.name 有66个文件。

咋整呢?懵了。

2. 属性类型突如其来的转变

另外一个例子,定义了用户的状态是否禁用的属性

type User{
    disable: boolean}
// 使用的地方
if(user.disable){
    // 用户已禁用 终止逻辑
    return;}

等你写了一大半了,后端说因为需求变了,用户的状态有 已禁用 未禁用 待禁用, 原本的布尔值支持不了需求了,要改成 number0 = 未禁用 1 = 已禁用 2 = 待禁用

先不改吧, 待禁用的用户也都无法操作了。

3. 无法为声明的业务数据扩展行为。

“长得一样”和“是”是两码事 (此项为补充。)

typeinterface 只能是声明业务数据的结构,但和对象是毫无关联的,无法使用对象内置的一些方法。

例如:

interface IUser {
  name: string}
type TUser = {
  name: string}
const userI: IUser = { name: 'Hamm' }
const userT: TUser = { name: 'Hamm' }
console.log(userI)
console.log(userT)

上述的 user 本身,还是个普通对象,只是满足了 User 的样子,但无法实现用户本身应有的行为,比如 eat

当然,你可能会这么搞:

interface IUser {
  name: string
  eat(): void}
const user: IUser = {
  name: 'Hamm',  eat() {
    console.log(`${this.name} eating...`)  },}user.eat()

乍一看,用 interface 也没啥毛病是吧?

再想想。这好像是 interface 约束了行为,而赋值的地方用 Object 来临时实现了 eat 的方法?

握巢,这好像才是 interface 应该有的功能啊~

那么,这也许才是正确的例子:

class User {
  name!: string  eat() {
    console.log(`${this.name} eating...`)  }}
const user = new User()
user.name = 'Hamm'user.eat()
console.log(user)

输出的截图:

再看看上面用类方式的打印信息, 能看到数据的具名类型,也能看到对象拥有的行为,岂不是美滋滋?

4. 例子还有很多

例子还会有很多,可以参考我之前另外一篇关于数据处理的文章:TypeScript装饰器之我们是这么处理项目数据转换的

我们统一使用 class 类 作为声明数据类型的方式,当然,之前评论区的一些问题我们也都考虑过了,那使用 class 的优势是什么?

1. 可以标记装饰器

众所周知,TypeScript 仅支持 class 类、属性、方法、参数等才能标记装饰器,至于标记装饰器了能干什么,之前我们也提到过了,可以继续翻阅本文所在专栏的其他文章。

2. 行为和属性共存

比如用户的结构,使用类可以声明属性,同时也可以直接声明方法:

class User{    nickName!: string        status!: number        isDisabled(){        return this.status === 1    }}

然后一些行为可以在类里面统一判断,而不需要在使用的地方自行判断,更不需要单独写个方法来判断用户是否被禁用:isDisabled 从某种意义上来说,不再是用户的行为,而是一种属性了。(当然,这里可以用 getter 来实现, 更像属性,只是我们不太喜欢 getter/setter, 而是用具体的方法。可以参考 Getter/Setter中那些有意思的小故事 )

因为可以写方法,我们可以利用 构造方法、链式调用等各种类的方式来完成很多好玩的需求,让写代码也可以变得很舒服。

3. 可以使用抽象类和抽象属性来实现更多设计模式

设计模式有多好玩相信很多后端开发者是很清楚的,当然,设计模式在前端也可以很好玩。

之前也有说过,TypeScript 比 Java 有意思的一个地方,就是 TypeScript 竟然把属性也可以给抽象掉~

4. 还要很多。。。

还有很多很多的好处,但这里必须要强调的一点是,很多人拿面向对象编程方式和函数式编程方式来抵触面向对象在前端存在的必要性,这里说明一下:

编程范式只是一种方式而已。

前端在函数式编程上加上面向对象,可能会更好玩,而且更有效率。

很多人说前端加上面向对象,后来的人不会维护,看不懂。

我一直没想过这个问题,今天思考了一下:

他看不懂关我啥事?面向对象很难吗就看不懂了?

用了。

interface 大量的用在了设计模式以及行为约束上,用于数据结构的只有一个地方,那就是装饰器的参数。

type 也用了,但大多数是用来起别名。。。

至于其他的数据结构,比如与第三方对接,那就用 Json 呗。用 interface 还有必要吗?

哦对了,我们封装了一个好玩的 interface, 叫 IJson,用来解决写 AnyScript 的问题:

export interface IJson<V = any> {
  /**   * JSON的键
   */

  [x: string]: V;}

希望你能看懂上面的讽刺代码。

相关前端面向对象开发的源代码可以参考:

Github: https://github.com/HammCn/AirPower4T

Gitee: https://gitee.com/air-power/AirPower4T

当然,也欢迎你阅读我的专栏:“用TypeScript写前端”。

点击关注公众号,“技术干货” 及时达!

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

TypeScript 前端开发 interface class 面向对象
相关文章