为什么在TypeScript上不使用interface/type来声明业务数据结构

一、写在前面

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

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

二、为什么不使用interfacetype

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

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

typescript 复制代码
// 客户用户
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个文件。

  • 直接替换 .name.realName 肯定不行了,很多都会炸。
  • 替换 User.name 也不行了,adminUser.name 也会被替换,而且有些人命名不规范,叫 a.name,会漏掉。

咋整呢?懵了。

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

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

typescript 复制代码
type User{
    disable: boolean
}

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

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

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

3. 例子还有很多

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

三、我们用什么?为什么?

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

1. 可以标记装饰器

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

2. 行为和属性共存

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

typescript 复制代码
class User{
    nickName!: string
    
    status!: number
    
    isDisabled(){
        return this.status === 1
    }
}

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

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

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

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

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

4. 还要很多。。。

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

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

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

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

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

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

四、我们用interfacetype了吗

用了。

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

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

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

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

typescript 复制代码
export interface IJson<V = any> {
  /**
   * JSON的键
   */
  [x: string]: V;
}

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

五、全文完

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

Github: github.com/HammCn/AirP...

Gitee: gitee.com/air-power/A...

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

相关推荐
LabVIEW开发7 小时前
LabVIEW QMH 队列消息处理架构
架构·labview·labview知识·labview功能·labview程序
代码搬运媛7 小时前
Jest 测试框架详解与实现指南
前端
counterxing8 小时前
我把 Codex 里的 Skills 做成了一个 MCP,还支持分享
前端·agent·ai编程
wangqiaowq8 小时前
windows下nginx的安装
linux·服务器·前端
rising start8 小时前
二、全面理解MySQL架构
mysql·架构
之歆9 小时前
DAY_12JavaScript DOM 完全指南(二):实战与性能篇
开发语言·前端·javascript·ecmascript
发现一只大呆瓜9 小时前
Vite凭什么这么快?3分钟带你彻底搞懂 Vite 热更新的幕后黑手
前端·面试·vite
麦客奥德彪9 小时前
Android Skills
架构·ai编程
Maimai108089 小时前
React如何用 @microsoft/fetch-event-source 落地 SSE:比原生 EventSource 更灵活的实时推送方案
前端·javascript·react.js·microsoft·前端框架·reactjs·webassembly
candyTong9 小时前
Claude Code 的 Edit 工具是怎么工作的
javascript·后端·架构