不是Typescript用不起,而是JSDoc更有性价比?

news2024/12/29 0:06:48

1. TS不香了?

4c7aab7ee61950015d6bc4c237e5feaa.jpeg

2023年,几条关于 Typescript 的新闻打破了沉寂,让没什么新活好整的前端开发圈子又热闹了一番。

b7d928cca77eb0bd3dbd74586c3c26c3.png

先是 GitHub 的报告称:“TypeScript 取代 Java 成为第三受欢迎语言”。

在其当年度 Octoverse 开源状态报告中,在最流行的编程语言方面,TypeScript 越来越受欢迎,首次取代 Java 成为 GitHub 上 OSS 项目中第三大最受欢迎的语言,其用户群增长了 37%。

而 Stack Overflow 发布的 2023 年开发者调查报告也显示,JavaScript 连续 11 年成为最流行编程语言,使用占比达 63.61%,TypeScript 则排名第五,使用占比 38.87%。

d5a01b785b01ce4c02bd645340691103.png

更大的争议则来自于:2023年9月,Ruby on Rails 作者 DHH 宣布移除其团队开源项目 Turbo 8 中的 TypeScript 代码

他认为,TypeScript 对他来说只是阻碍。不仅因为它需要显式的编译步骤,还因为它用类型编程污染了代码,很影响开发体验。

无独有偶,不久前,知名前端 UI 框架 Svelte 也宣布从 TypeScript 切换到 JavaScript。负责 Svelte 编译器的开发者说,改用 JSDoc 后,代码不需要编译构建即可进行调试 —— 简化了编译器的开发工作。

Svelte 不是第一个放弃 TypeScript 的前端框架。早在 2020 年,Deno 就迁移了一部分內部 TypeScript 代码到 JavaScript,以减少构建时间。

如此一来,今年短期内已经有几个项目从 TypeScript 切换到 JavaScript 了,这个状况就很令人迷惑。难道从 TypeScript 切回 JavaScript 已经成了当下的新潮流?这难道不是在开历史的倒车吗?

TypeScript

由微软发布于 2012 年的 TypeScript,其定位是 JavaScript 的一个超集,它的能力是以 TC39 制定的 ECMAScript 规范为基准(即 JavaScript )。业内开始用 TypeScript 是因为 TypeScript 提供了类型检查,弥补了 JavaScript 只有逻辑没有类型的问题,

对于大型项目、多人协作和需要高可靠性的项目来说,使用 TypeScript 是很好的选择;静态类型检查的好处,主要包括:

  • 类型安全

  • 代码智能感知

  • 重构支持

而 TS 带来的主要问题则有:

  • 某些库的核心代码量很小,但类型体操带来了数倍的学习、开发和维护成本

  • TypeScript 编译速度缓慢,而 esbuild 等实现目前还不支持装饰器等特性

  • 编译体积会因为各种重复冗余的定义和工具方法而变大

相比于 Svelte 的开发者因为不厌其烦而弃用 TS 的事件本身,其改用的 JSDoc 对于很多开发者来说,却是一位熟悉的陌生人。

2. JSDoc:看我几分像从前?

早在 1999 年由 Netscape/Mozilla 发布的 Rhino -- 一个 Java 编写的 JS 引擎中,已经出现了类似 Javadoc 语法的 JSDoc 雏形

Michael Mathews 在 2001 年正式启动了 JSDoc 项目,2007 年发布了 1.0 版本。直到 2011 年,重构后的 JSDoc 3.0 已经可以运行在 Node.js 上

JSDoc 语法举例

定义对象类型:

/**
 * @typedef {object} Rgb
 * @property {number} red
 * @property {number} green
 * @property {number} blue
 */

/** @type {Rgb} */
const color = { red: 255, green: 255, blue: 255 };

定义函数类型:

/**
 * @callback Add
 * @param {number} x
 * @param {number} y
 * @returns {number}
 */
const add = (x, y) => x + y;

定义枚举:

/**
 * Enumerate values type
 * @enum {number}
 */
const Status = {
  on: 1,
  off: 0,
};

定义类:

class Computer {
  /**
   * @readonly Readonly property
   * @type {string}
   */
  CPU;

  /**
   * @private Private property
   */
  _clock = 3.999;

  /**
   * @param {string} cpu
   * @param {number} clock
   */
  constructor(cpu, clock) {
    this.CPU = cpu;
    this._clock = clock;
  }
}

在实践中,多用于配合 jsdoc2md 等工具,自动生成库的 API 文档等。

随着前后端分离的开发范式开始流行,前端业务逻辑也日益复杂,虽然不用为每个应用生成对外的 API 文档,但类型安全变得愈发重要,开发者们也开始尝试在业务项目中使用 jsdoc。但不久后诞生的 Typescript 很快就接管了这一进程。

但前面提到的 TS 的固有问题也困扰着开发者们,直到今年几起标志性事件的发生,将大家的目光拉回 JSDoc,人们惊讶地发现:JSDoc 并没有停留在旧时光中。

吾谓大弟但有武略耳,至于今者,学识英博,非复吴下阿蒙

除了 JSDoc 本身能力的不断丰富,2018 年发布的 TypeScript 2.9 版本无疑是最令人惊喜的一剂助力;该版本全面支持了将 JSDoc 的类型声明定义成 TS 风格,更是支持了在 JSDoc 注释的类型声明中动态引入并解析 TS 类型的能力。

456c7b353ae327b881251a83f7abe3c7.png

比如上文中的一些类型定义,如果用这种新语法,写出来可以是这样的:

定义对象类型:

/**
 * @typedef {{ brand: string; color: Rgb }} Car
 */

/** @type {Rgb} */
const color = { red: 255, green: 255, blue: 255 };

定义函数类型:

/**
 * @typedef {(x: number, y: number) => number} TsAdd
 */

/** @type {TsAdd} */
const add = (x, y) => x + y;

TS 中的联合类型等也可以直接用:

/**
 * Union type with pipe operator
 * @typedef {Date | string | number} MixDate
 */

/**
 * @param {MixDate} date
 * @returns {void}
 */
function showDate(date) {
  // date is Date
  if (date instanceof Date) date;
  // date is string
  else if (typeof date === 'string') date;
  // date is number
  else date;
}

范型也没问题:

/**
 * @template T
 * @param {T} data
 * @returns {Promise<T>}
 * @example signature:
 * function toPromise<T>(data: T): Promise<T>
 */
function toPromise(data) {
  return Promise.resolve(data);
}

/**
 * Restrict template by types
 * @template {string|number|symbol} T
 * @template Y
 * @param {T} key
 * @param {Y} value
 * @returns {{ [K in T]: Y }}
 * @example signature:
 * function toObject<T extends string | number | symbol, Y>(key: T, value: Y): { [K in T]: Y; }
 */
function toObject(key, value) {
  return { [key]: value };
}

类型守卫:

/**
 * @param {any} value
 * @return {value is YOUR_TYPE}
 */
function isYourType(value) {
 let isType;
 /**
  * Do some kind of logical testing here
  * - Always return a boolean
  */
 return isType;
}

至于动态引入 TS 定义也很简单,不管项目本身是否支持 TS,我们都可以放心大胆地先定义好类型定义的 .d.ts 文件,如:

// color.d.ts
export interface Rgb {
  red: number;
  green: number;
  blue: number;
}

export interface Rgba extends Rgb {
  alpha: number;
}

export type Color = Rgb | Rbga | string;

然后在 JSDoc 中:

// color.js 
/** @type {import('<PATH_TO_D_TS>/color').Color} */
const color = { red: 255, green: 255, blue: 255, alpha: 0.1 };

当然,对于内建了基于 JSDoc 的类型检查工具的 IDE,比如以代表性的 VSCode 来说,其加持能使类型安全锦上添花;与 JSDoc 类型(即便不用TS语法也可以)对应的 TS 类型会被自动推断出来并显示、配置了 //@ts-check后可以像 TS 项目一样实时显示类型错误等。这些都很好想象,在此就不展开了。

JSDoc 和 TS 能力的打通,意味着前者书写方式的简化和现代化,成为了通往 TS 的便捷桥梁;也让后者有机会零成本就能下沉到业内大部分既有的纯 JS 项目中,这路是裤衩一下子就走宽了。

3. 用例:Protobuf+TS 的渐进式平替

既然我们找到了一种让普通 JS 项目也能快速触及类型检查的途径,那也不妨想一想对于在那些短期内甚至永远不会重构为 TS 的项目,能够复刻哪些 TS 带来的好处呢?

对于大部分现代的前后端分离项目来说,一个主要的痛点就是核心的业务知识在前后端项目之间是割裂的。前后端开发者根据 PRD 或 UI,各自理解业务逻辑,然后总结出各自项目中的实体、枚举、数据派生逻辑等;这些也被成为领域知识或元数据,其割裂在前端项目中反映为一系列问题:

  • API 数据接口的入参、响应类型模糊不清

  • 表单项的很多默认值需要硬编码、多点维护

  • 前后端对于同一概念的变量或动作命名各异

  • mock 需要手写,并常与最后实际数据结构不符

  • TDD缺乏依据,代码难以重构

  • VSCode 中缺乏智能感知和提示

对于以上问题,比较理想的解决方法是前端团队兼顾 Node.js 中间层 BFF 的开发,这样无论是组织还是技术都能最大程度通用。

  • 但从业内近年的诸多实践来看,这无疑是很难实现的:即便前端团队有能力和意愿,这样的 BFF 模式也难以为继,此中既有 Node.js 技术栈面临复杂业务不抗打的问题,更多的也有既有后端团队的天然抗拒问题。

  • 一种比较成功的、前后端接受度都较好的解决方案,是谷歌推出的 ProtoBuf。

在通常的情况下,ProtoBuf(Protocol Buffers)的设计思想是先定义 .proto 文件,然后使用编译器生成对应的代码(例如 Java 类和 d.ts 类型定义)。这种方式确保了不同语言之间数据结构的一致性,并提供了跨语言的数据序列化和反序列化能力

  • 但是这无疑要求前后端团队同时改变其开发方式,如果不是从零起步的项目,推广起来还是有一点难度

因此,结合 JSDoc 的能力,我们可以设计一种退而求其次、虽不中亦不远矣的改造方案 -- 在要求后端团队写出相对比较规整的实体定义等的前提下,编写提取转换脚本,定期或手动生成对应的 JSDoc 类型定义,从而实现前后端业务逻辑的准确同步。

e0f0b8f3aee7bc8056cb2274e603bc23.png

比如,以一个Java的BFF项目为例,可以做如下转换

枚举:

public enum Color {
    RED("#FF0000"), GREEN("#00FF00"), BLUE("#0000FF");

    private String hexCode;

    Color(String hexCode) {
        this.hexCode = hexCode;
    }

    public String getHexCode() {
        return hexCode;
    }
}

public enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

转换为:

/**
 * @readonly
 * @enum {String}
 */
export const Color = {
  RED: '#FF0000',
  GREEN: '#00FF00',
  BLUE: '#0000FF',
}

/**
 * @readonly
 * @enum {Number}
 */
export const Day = {
  MONDAY: 0,
  TUESDAY: 1,
  WEDNESDAY: 2,
  THURSDAY: 3,
  FRIDAY: 4,
  SATURDAY: 5,
}

POJO:

public class MyPojo {
 private Integer id;
 private String name;

 public Integer getId() {
  return id;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }
}

转换为:

/**
 * @typedef {Object} MyPojo
 * @property {Integer}  [id]
 * @property {String}  [name]
*/

在转换的方法上,理论上如果能基于 AST 等手段当然更好,但如本例中的 Java 似乎没有特别成熟的转换工具,java-parser 等库文档资料又过少。

而基于正则的转换虽然与后端具体写法耦合较大,但也算简单灵活。这里给出一个示例 demo 项目供参考:https://github.com/tonylua/java-to-type

e4093ea5b0f09ed437d272d70b89ffb8.png

7c8980284cb9bd0c8781460bb3a5e82c.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1289025.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何通过navicat连接SQL Server数据库

本文介绍如何通过Navicat 连接SQL Server数据库。如果想了解如何连接Oracle数据库&#xff0c;可以参考下边这篇文章。如何通过Navicat连接Oracle数据库https://sgknight.blog.csdn.net/article/details/132064235 1、新建SQL Server连接配置 打开Navicat软件&#xff0c;点击…

【微信小程序开发】学习小程序的模块化开发(自定义组件和分包加载)

前言 模块化开发是一种将复杂的应用程序分解为一系列独立的模块&#xff0c;每个模块负责完成特定的功能的开发方式。模块化开发可以提高代码的可维护性和可复用性&#xff0c;使开发过程更加高效和灵活。 文章目录 前言模块化开发的重要性和优势自定义组件自定义组件的概念和作…

观测云实现日志存储与分析 10 倍性价比提升|SelectDB 技术团队

作者&#xff1a;观测云 CEO 蒋烁淼 & 飞轮科技技术团队 在云计算逐渐成熟的当下&#xff0c;越来越多的企业开始将业务迁移到云端&#xff0c;传统的监控和故障排查方法已经无法满足企业的需求。而观测云可提供整体数据的分析、洞察、可视化、自动化、监测告警、智能巡查…

【EI征稿中#先投稿,先送审#】第三届网络安全、人工智能与数字经济国际学术会议(CSAIDE 2024)

第三届网络安全、人工智能与数字经济国际学术会议&#xff08;CSAIDE 2024&#xff09; 2024 3rd International Conference on Cyber Security, Artificial Intelligence and Digital Economy 第二届网络安全、人工智能与数字经济国际学术会议&#xff08;CSAIDE 2023&…

使用Selenium模拟人工操作及获取网页内容

使用Selenium抓取网页动态内容 根据权威机构发布的全球互联网可访问性审计报告&#xff0c;全球约有四分之三的网站其内容或部分内容是通过JavaScript动态生成的&#xff0c;这就意味着在浏览器窗口中“查看网页源代码”时无法在HTML代码中找到这些内容&#xff0c;也就是说我们…

Python创建交互式Web应用:Shiny库详解

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com Shiny是一个基于Python的交互式Web应用框架&#xff0c;专注于简化Web应用的开发流程。本文将深入探讨Shiny库的基本用法、高级功能以及实际应用案例&#xff0c;以帮助开发者充分发挥Shiny在Web应用开发中的优势…

周大福传世杰作「裕世钻芳华」首次亮相“超越时光”天然钻石展

&#xff08;2023年12月6日&#xff0c;北京&#xff09;天然钻石&#xff0c;是自地球深处历经数十亿年时光形成的自然奇迹&#xff0c;在悠长的岁月中见证了无数真挚情感的珍贵瞬间。12月6日&#xff0c;“超越时光”周大福天然钻石展于北京凤凰国际传媒中心启幕&#xff0c;…

基于深度学习YoloV8的火焰烟雾检测系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介简介YoloV8模型火焰烟雾检测系统模型训练实时检测 应用领域 二、功能三、系统四. 总结 一项目简介 # 基于深度学习YoloV8的火焰烟雾检测系统介绍 简介 深…

JIRA 禁用用户自动登录

概述 当用户登录 JIRA 时&#xff0c;他们可以通过在单击“Log In”按钮之前选中“Remember my login”复选框&#xff0c;让 JIRA 记住他们的登录信息。这样做之后&#xff0c;“Remember my login”令牌将由 JIRA 服务器存储&#xff0c;并且系统会在用户的浏览器中设置包含…

OpenHarmony北向-让更广泛的应用开发者更容易参与

一、标准系统的体验 按照官方文档指导&#xff0c;这样操作&#xff0c;OH标准系统开发板就可以运行开发者开发的OpenHarmony应用了。 二、实际情况 按照开发文档上的说明&#xff0c;肯定是装不上的。因为OH不同的发行版&#xff0c;不同发行板不同的设备&#xff0c;IDE&…

第一篇:MongoDB的安装、启动、关闭、链接shell

目录 简介 安装 安装遇到的问题 查看brew 当前使用的源&#xff1a; 更换brew 源。更换成清华大学镜像源 版本查看 MongoDB 数据目录与日志目录 启动方式一&#xff1a; 启动MongoDB 验证MongoDB 是否正常运行 停止或重新启动 停止MongoDB 服务 重新启动MongoDB服…

阿里测试8年,肝到P8只剩我一个了····

在阿里工作了8年&#xff0c;工作压力大&#xff0c;节奏快&#xff0c;但是从技术上确实得到了成长&#xff0c;尤其是当你维护与大促相关的系统的时候&#xff0c;熬到P7也费了不少心思&#xff0c;小编也是个爱学习的人&#xff0c;把这几年的工作经验整理成了一份完整的笔记…

农用烘干机市场分析:我国市场规模为190亿元

农用烘干机是运用在农业烘干领域的传统干燥设备之一&#xff0c;主要是为了农副产品直接使用或满足进一步加工的需要。农用烘干机具有操作弹性大、适应性强、处理能力大、设备运转可靠等优点&#xff0c;能大幅度提高农副产品烘干效率。现阶段&#xff0c;我国农用烘干机的市场…

【面试经典150 | 二叉树】翻转二叉树

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;递归方法二&#xff1a;迭代 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题…

力扣每日一题:2477. 到达首都的最少油耗(2023-12-05)

力扣每日一题 题目&#xff1a;2477. 到达首都的最少油耗 日期&#xff1a;2023-12-05 用时&#xff1a;34 m 15 s 时间&#xff1a;37ms 内存&#xff1a;84.8MB 思路&#xff1a;分别计算每条路上通过的城市数量&#xff08;数量/座位数&#xff0c;向上取整&#xff09;&…

Python-赋值运算符(详解)

表示赋值 左侧为变量&#xff0c;右边为值 a b 10#先把10赋值给b&#xff0c;再把b赋值给a 相当于a 10 b 10 链式赋值&#xff0c;但是不推荐&#xff0c;一般一行一个语句&#xff0c;提高可读性&#xff0c;良好的代码风格 多元赋值&#xff1a; a , b 10,20 #python语…

C# WPF上位机开发(计算器界面设计)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 c# wpf最大的优势就是开发业务软件比较快、效率比较高。一般来说&#xff0c;它的界面和逻辑部分可以同时开发。界面的部分用xaml编写即可&#xf…

laravel记录mysql日志最便捷的办法

因为页面执行的sql很多&#xff0c;因此决定记录一下执行的sql语句。最简便快速的方式就是使用下面的代码&#xff1a; app\Providers\AppServiceProvider.php 在boot方法里面加上下面的代码&#xff1a; \DB::listen(function ($query) {$tmp str_replace(?, ".%s.&quo…

c语言编译优化引发问题

问题描述 同样的代码,不优化编译,可以正常执行,经过-O2优化编译后,代码被卡住.整体功能涉及多进程,多线程操作. 问题发现 经过加打印,发现卡在while(a!0);//死循环,等待特殊事件发生来解开循环 a初始化为-1; 过一会后,另外有个线程,当特定事件发生的时候,将a置为0; 通过加打…

万界星空科技MES系统在工业生产中的应用

万界星空科技MES系统在工业生产中的应用广泛。它适用于各类制造业&#xff0c;包括汽车制造、电子制造、注塑、能源化工、航天航空、食品加工、服装纺织、灯具、电线电缆、电机发动机、印刷包装等行业。 在汽车制造领域&#xff0c;MES系统可以实时追踪和控制整个生产过程&…