用 TS 来写 Node.js CLI 是什么体验?

1,673 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

背景

目前,前端开发常用的 Node.js CLI 工具中,绝大部分其实都是 JS 梭哈写的:

  • yarn
  • @vue/cli
  • create-react-app
  • nrm
  • webpack
  • @babel/cli
  • 。。。

而用 TS 来实现的,少得可怜:

所以我搭建了一个基于 TS 的 Node.js CLI 脚手架,让写 Node.js CLI 多一个选择。

好处

那么,用 TS 来写 Node.js CLI,到底有啥好处呢?我觉得有以下 3 点:

享受 TS 带来的开发体验

在开发过程中,可以享受 TS 的语法声明提示、类型校验等好处,有更好的开发体验。如下图,当我们在调用已定义好的方法时,可以很清楚的看到所调用方法的参数以及参数类型:

而且如果我们传递的参数不符合函数要求,也会抛出具体的错误:

并且在打包后,每一个以 .ts 结尾的源代码都会打包出来一个以 .d.ts 结尾的文件,这个就是 TS 自动生成的语法声明文件,大部分情况下都可直接使用~

更多 TS 的使用方式可移步 TS 官网学习~

更好的兼容性

打包后的 JS 代码,是将源代码打包成了 JS 的 ES2015 版本,可兼容更低版本的 Node.js 运行。打包前的代码是这样的:

export const exec = (cmd: string, options = {}): Promise<any> => {
  return new Promise((resolve, reject) => {
    const stdout = shell.exec(cmd, options, (error: number | null, stdout: string, stderr: string) => {
      if (error) {
        reject(error)
      }
      resolve({ error, stdout, stderr })
    })
    stdout.on('exit', (code: number | null, signal: NodeJS.Signals | null) => {
      resolve({ code, signal })
    })
  })
}

打包后:

var exec = function (cmd, options) {
    if (options === void 0) { options = {}; }
    return new Promise(function (resolve, reject) {
        var stdout = shell.exec(cmd, options, function (error, stdout, stderr) {
            if (error) {
                reject(error);
            }
            resolve({ error: error, stdout: stdout, stderr: stderr });
        });
        stdout.on('exit', function (code, signal) {
            resolve({ code: code, signal: signal });
        });
    });
};
exports.exec = exec;

使用新的语法糖

你可以在开发中使用扩展运算符、class 类、async/await 等新的 JS 语法糖:

class A {
  b () {
    return 1
  }
}

console.log([1, 2, 3, ...[1, 2, 3]])

至于新语法糖的兼容问题,TS 早已在打包后替我们解决了。上面的 class 类和扩展运算符在打包后,已经变成了这样:

var A = (function () {
    function A() {
    }
    A.prototype.b = function () {
        return 1;
    };
    return A;
}());
console.log(__spreadArray([1, 2, 3], [1, 2, 3], false));

缺点

当然,使用 TS 来写 Node.js CLI,它也带来一些缺点:

  • 编译后的代码可读性低,调试也相对麻烦
  • 有学习成本,需要学习 TS 使用方式

针对这个学习成本,如果你本来就是想学 TS,那就不是啥缺点了。

使用场景

那么,要不要使用 TS 来进行 Node.js CLI 开发呢?

如果你有以下两种场景:

  • 想尝鲜、想学习 TS
  • 开发的 CLI 工具需要对外提供 API

那我推荐你试试~

注意事项

如果我想试试,那使用 TS 来开发 Node.js CLI 需要注意些什么呢?

某些 NPM 包需要手动导入语法声明文件

如果你引入一个包在调用它的方法时没有显示出来语法提示,那多半是这个包没有自带 TS 声明文件,你可以在 TS 官方 types 包搜索入口 中搜索一下是否有可用的 @types 包,例如这里我搜索了一下 lodash:

其实大部分包的声明文件都可以在 @types 中找到~

import 包方式改变

使用 TS 后,很多 NPM 包并没有提供模块化的导出,需要将这一类包 import 方式改成这样:

import * as chalk from 'chalk'
import * as ora from 'ora'
import * as shell from 'shelljs'

// ...

发布的时候不要带上源码

这个可以在 package.json 里面中设置 files 字段解决:

{
  "files": [
    "/bin",
    "/lib"
  ]
}

尽量不要写 any

使用 TS 很大一部分原因就是为了友好的语法提示和严格的类型声明,

如果 any 写多了,使用 TS 也就失去了意义,

就像肉夹馍没了肉。。。

常用的 Node.js CLI NPM 包推荐

  • commander:用于命令配置和参数解析,类似的还有 caporalyargscacoptionator
  • chalk:用于多种颜色的日志输出
  • dotenv:用于环境变量解析
  • http-server:用于命令行启动静态 HTTP 服务器
  • inquirer:用于命令行交互
  • lodash:鼎鼎大名的 JS 工具库,包很大,建议按需导入
  • update-notifier:用于 CLI 工具自动更新提示
  • download-git-repo:用于下载 Github 上的代码,通过命令创建脚手架时经常用到
  • shelljs:用于调用系统命令,例如 cdcplsrm 等,兼容 Windows 和 Linux
  • ora:用于创建加载效果,相当于 React 组件中的 Loading 组件

更多Node.js CLI NPM 包推荐

使用 wy-cli 创建脚手架

看完了上面的优缺点以及注意事项后,如果你想尝尝鲜或者想学习一下 TS,亦或者你需要写一个对外提供 API 的 Node.js CLI,那不妨试试我的脚手架吧,全局安装:

npm i @wytxer/wy-cli -g

然后执行 wy init project-name 命令安装脚手架,在列表中选择命令行脚手架:

❯ 命令行脚手架(Node.js + TypeScript + Commander)

脚手架常用命令:

# 安装
yarn install

# 启动开发
yarn dev

# 打包
yarn build

# 发布
npm publish

代码和文档

感谢阅读,脚手架以后会长期维护,欢迎下载使用:脚手架 GitHub 地址

脚手架使用文档也会保持同步更新。

如果你在使用过程中发现有问题,欢迎提交 issue 反馈~