The Copy Monkey
HomeResourceTrendDemoAboutLink

Made with ❤️ by bhwang

© 2025 The Copy Monkey. All rights reserved.

渐进式迁移项目到TypeScript

2022-05-20

上次写文章已经是一年前了, 来拔下草...

前言

维护一个有几年历史的 JavaScript 项目是非常辛苦的(如果碰巧这个项目之前的开发不怎么喜欢写文档和注释, 就更难了)。

假设你需要给里面一部分功能打些补丁, 那么你常常就会面对如下场景:WTF这个对象有啥属性? 通过一堆console.log和断点调试终于搞定了, 发誓再也不碰这段代码了。

几个月以后又有需求需要在这里打些补丁, 然后就会再次出现:WTF这个对象有啥属性? ...

也想过引入 TypeScript 来解决以上问题, 它能提供 IDE 自动补全还有最重要的类型安全。但是面对 1000+ 个 js 文件, 算了算了。

这篇文章提供了一种渐进式引入 TypeScript 的方式, 可供大家参考。

给 JavaScript 加上「类型注释」

秘诀就是JSDoc, 其实很多 JavaScript 代码编辑器都支持 JSDoc, 比如我常用的 Visual Studio Code 和 Webstorm 。

下面用一个案例来介绍具体怎么使用 JSDoc 。

一个原始 JavaScript 文件

假设我们有一个 meeting.js 文件, 描述了如何将成员渲染到页面上。里面大致代码如下:

// meeting.js

// 成员
let members = [{
    id: '001', nickName: 'wang', role = 'speaker'
}];
// 视频流
let streams = {
    '001': MagicStreamObject()
};

function renderMeetingMembers(members, streamSources) {
    // 发言人
    let speakers = members.filter(x => x.role === 'speaker');
    // 其他人
    let others = members.filter(x => x.role !== 'speaker');

    // 将成员与流关联起来
    speakers.forEach(speaker => {
        let stream = streams[speaker.id];
        if (stream) {
            speaker.stream = stream;
        }
    });

    // 非常复杂的渲染逻辑
    renderMembers(speakers, others);
}

renderMeetingMembers(members, streams);

要给上面这个文件加上「类型注释」应该怎么做呢?

定义一个 .d.ts 文件

因为上面两个代码编辑器都默认支持 TS 文件, 那么我们可以方便的在项目里新建任意 TS 文件。里面大致代码如下

// meeting.d.ts
export interface MagicStream {
    play: () => void;
    stop: () => void;
};

export type Member {
    id: string;
    nickName: string;
    role: 'speaker' | 'role';
    stream?: MagicStream;
};

至于什么时候用 type, 什么时候用 interface 随便你...

引入类型并标注

第一步, 引入类型。使用 @typedef 和 import

// meeting.js
/** @typedef {import('./meeting.d.ts').MagicStream} MagicStream */
/** @typedef {import('./meeting.d.ts').Member} Member */

第二步, 用 @type 给普通变量加上类型

/** @type {Member[]} */
let members = [{ id: '001', nickName: 'wang', role = 'speaker' }];
/** @type {{ [userId: string]: MagicStream }} */
let streams = {
    '001': MagicStreamObject()
};

第三步, 给函数参数加上注释

/**
 * @param {Member[]}  members - 参会成员
 * @param {{ [userId: string]: MagicStream }}  streamSources - 流
 **/
function renderMeetingMembers(members, streamSources) {
    // blabla
}

做完这几步以后基本上编辑器就能自动补全代码了!!!

引入 TSC

随着 d.ts 文件积累越来越多,是时候开始真正引入 TSC 来编译整个项目了。

去网上随便抄个教程吧,注意关键点是 tsconfig.json 里面有个重要选项:allowJS - 混写 JavaScript 和 TypeScript 。

把 js 文件换成 ts

没啥特别的难度, 一点点修复警告⚠️。实在不行 any 大法好!!!