02-工欲善其事:打造最舒适的 TypeScript 开发环境
课程
1
开篇:用正确的方式学习 TypeScript
已学完
学习时长: 7分24秒
2
工欲善其事:打造最舒适的 TypeScript 开发环境
学习时长: 19分14秒
3
进入类型的世界:理解原始类型与对象类型
学习时长: 37分40秒
4
掌握字面量类型与枚举,让你的类型再精确一些
学习时长: 24分53秒
5
函数与 Class 中的类型:详解函数重载与面向对象
学习时长: 50分40秒
6
探秘内置类型:any、unknown、never 与类型断言
学习时长: 34分58秒
7
类型编程好帮手:TypeScript 类型工具(上)
学习时长: 33分34秒
8
类型编程好帮手:TypeScript 类型工具(下)
学习时长: 40分52秒
9
类型编程基石:TypeScript 中无处不在的泛型
学习时长: 43分46秒
10
结构化类型系统:类型兼容性判断的幕后
学习时长: 20分48秒
11
类型系统层级:从 Top Type 到 Bottom Type
学习时长: 45分16秒
12
类型里的逻辑运算:条件类型与 infer
学习时长: 52分44秒
13
内置工具类型基础:别再妖魔化工具类型了!
学习时长: 44分47秒
14
反方向类型推导:用好上下文相关类型
学习时长: 18分37秒
15
函数类型:协变与逆变的比较
学习时长: 21分38秒
16
了解类型编程与类型体操的意义,找到平衡点
学习时长: 4分27秒
17
内置工具类型进阶:类型编程进阶
学习时长: 83分3秒
18
基础类型新成员:模板字符串类型入门
学习时长: 32分58秒
19
类型编程新范式:模板字符串工具类型进阶
学习时长: 69分27秒
20
工程层面的类型能力:类型声明、类型指令与命名空间
学习时长: 53分23秒
21
在 React 中愉快地使用 TypeScript:内置类型与泛型坑位
学习时长: 78分9秒
22
让 ESLint 来约束你的 TypeScript 代码:配置与规则集介绍
学习时长: 57分13秒
23
全链路 TypeScript 工具库,找到适合你的工具
学习时长: 25分13秒
24
说说 TypeScript 和 ECMAScript 之间那些事儿
学习时长: 29分14秒
25
装饰器与反射元数据:了解装饰器基本原理与应用
学习时长: 81分53秒
26
控制反转与依赖注入:基于装饰器的依赖注入实现
学习时长: 69分6秒
27
TSConfig 全解(上):构建相关配置
学习时长: 55分33秒
28
TSConfig 全解(下):检查相关、工程相关配置
学习时长: 76分19秒
29
基于 Prisma + NestJs 的 Node API :前置知识储备
学习时长: 44分50秒
30
基于 Prisma + NestJs 的 Node API :项目开发与基于 Heroku 部署
学习时长: 46分40秒
31
玩转 TypeScript AST:AST Checker 与 CodeMod
学习时长: 85分18秒
32
感谢相伴:是结束,也是开始
学习时长: 3分21秒
33
漫谈篇:面试中的 TypeScript
学习时长: 4分25秒
juejin_logo copyCreated with Sketch.

在正式开始小册的学习前,我们还有一件事要做,那就是搭建 TypeScript 的开发环境。一个舒适、便捷且顺手的开发环境,不仅能大大提高学习效率,也会对我们日常的开发工作有很大帮助。

这一节我们就来介绍 VS Code 下的 TypeScript 环境搭建:插件以及配置项。对于 TS 文件的执行,我们会介绍 ts-node、ts-node-dev 等工具,帮助你快速验证 TS 代码的执行结果。而如果你只想快速开始学习,我们也会介绍 TypeScript 官方提供的 TypeScript Playground,利用它你可以快速开始编写及分享 TS 代码。最后,我们还会介绍如何通过 TS 声明的方式来检查类型兼容性。

话不多说,我们快点开始吧~

本节代码见:Starter

VS Code 配置与插件

VS Code 本身就是由 TypeScript 编写的,因此它对 TypeScript 有着非常全面的支持,包括类型检查、补全等功能,我们需要的两个 TS 插件都来自于社区,这两个插件分别提供了类型的自动导入,和快速移动 TypeScript 文件的能力。

首先是 TypeScript Importer 。这一插件会收集你项目内所有的类型定义,在你敲出:时提供这些类型来进行补全。如果你选择了一个,它还会自动帮你把这个类型导入进来。效果如图所示:

img

这一功能在日常开发中真得非常非常好用,尤其是当项目里有数百个声明分散在各个文件中时。

Move TS,这一插件在重构以及像我们这样写 demo 的场景下很有帮助。它可以让你通过编辑文件的路径,直接修改项目的目录结构。比如从home/project/learn-interface.ts 修改成 home/project/interface-notes/interface-extend.ts,这个插件会自动帮你把文件目录更改到对应的样子,并且更新其他文件中对这一文件的导入语句。

usage.gif

当然,对于 VS Code 内置的 TypeScript 支持,我们也可以通过一些配置项获得更好的开发体验。首先,你需要通过 Ctrl(Command) + Shift + P 打开命令面板,找到「打开工作区设置」这一项。

open setting

然后,在打开的设置中输入 typescript,筛选出所有 TypeScript 有关的配置,点击左侧的"TypeScript",这里才是官方内置的配置。

我们需要做的就是开启一些代码提示功能(hints),我们知道 TS 能够在很多地方进行类型地自动推导,但你往往要把鼠标悬浮在代码上才能看到推导得到的类型,其实我们可以通过配置将这些推导类型显示出来:

在前面配置搜索处,搜索 'typescript Inlay Hints',展示的配置就都是提示相关的了,推荐开启的有这么几个:

  • Function Like Return Types,显示推导得到的函数返回值类型;
  • Parameter Names,显示函数入参的名称;
  • Parameter Types,显示函数入参的类型;
  • Variable Types,显示变量的类型。

以上选项开启后的效果如下:

eg

当然,并不是所有人都习惯这样的显示方式,你可以根据自己的需要进行调整。除了这些提示的配置以外,VS Code 还支持了百余项 TS 配置,你可以看看是否有你需要的配置。

其他插件

除了 TS 强相关的插件与配置,还有一些额外的、能提升你学习效率的插件,你可以依据自己的喜好进行添加,以下的插件列表将会不定期进行更新。

  • ErrorLens,这一插件能够把你的 VS Code 底部问题栏的错误下直接显示到代码文件中的对应位置,比如这样:

Playground:懒人福音

如果你只是想拥有一个简单的环境,能写 TypeScript,能检查错误,能快速地调整 tsconfig,那官方提供的 Playground 一定能满足你的需求。

你可以在这里编写 TS 代码,快速查看编译后的 JS 代码与声明文件,还可以通过 Shift + Enter 来执行 TS 文件。可以说,如果不需要 VS Code 更强大的提示能力与一些特殊插件、主题等,Demo 学习使用 Playground 真的够够的了。

Playground 最强大的能力其实在于,支持非常简单的配置切换,如 TS 版本(左上角 ),以及通过可视化的方式配置 tsconfig (左上角的配置)等,非常适合在这里研究 tsconfig 各项配置的作用。

TS 文件的快速执行:ts-node 与 ts-node-dev

当然,如果你主要是想执行 TypeScript 文件,就像 node index.js 这样快速地验证代码逻辑,这个时候你就需要 ts-node 以及 ts-node-dev 这一类工具了。它们能直接执行 ts 文件,并且支持监听文件重新执行。同时,它们也支持跳过类型检查这一步骤来获得更快的执行体验。

对于 ts-node,你可以将其安装到项目本地或直接全局安装,我个人更推荐安装到全局然后配置 alias 快速启动,像 tsn index.ts 这样。执行以下命令将 ts-node 与 typescript 安装到全局:

$ npm i ts-node typescript -g

然后,在项目中执行以下命令创建 TypeScript 的项目配置文件: tsconfig.json。

npx --package typescript tsc --init
// 如果全局安装了 TypeScript,可以这么做
tsc --init

接着,创建一个 TS 文件:

console.log("Hello TypeScript");

再使用 ts-node 执行:

ts-node index.ts

如果一切正常,此时你的终端能够正确地输出字符。ts-node 可以通过两种方式进行配置,在 tsconfig 中新增 'ts-node' 字段,或在执行 ts-node 时作为命令行的参数,这里我们主要介绍通过命令行进行常用配置的方式。

  • -P,--project:指定你的 tsconfig 文件位置。默认情况下 ts-node 会查找项目下的 tsconfig.json 文件,如果你的配置文件是 tsconfig.script.jsontsconfig.base.json 这种,就需要使用这一参数来进行配置了。
  • -T, --transpileOnly:禁用掉执行过程中的类型检查过程,这能让你的文件执行速度更快,且不会被类型报错卡住。这一选项的实质是使用了 TypeScript Compiler API 中的 transpileModule 方法,我们会在后面的章节详细讲解。
  • --swc:在 transpileOnly 的基础上,还会使用 swc 来进行文件的编译,进一步提升执行速度。
  • --emit:如果你不仅是想要执行,还想顺便查看下产物,可以使用这一选项来把编译产物输出到 .ts-node 文件夹下(需要同时与 --compilerHost 选项一同使用)。

除了直接使用 ts-node 以外,你也可以通过 node + require hook 的形式来执行 TS 文件:

node -r ts-node/register index.ts

但此时,如果想要传递参数给 ts-node ,你就需要使用环境变量了,比如要传递之前的 transpileOnly 选项:

TS_NODE_TRANSPILE_ONLY=true node -r ts-node/register index.ts

关于选项对应的环境变量,请参考 ts-node 的官方文档了解更多。

ts-node 本身并不支持自动地监听文件变更然后重新执行,而这一能力又是某些项目场景下的刚需,如 NodeJs API 的开发。因此,我们需要 ts-node-dev 库来实现这一能力。ts-node-dev 基于 node-dev(你可以理解一个类似 nodemon 的库,提供监听文件重新执行的能力) 与 ts-node 实现,并在重启文件进程时共享同一个 TS 编译进程,避免了每次重启时需要重新实例化编译进程等操作。

首先,我们还是在全局安装:

npm i ts-node-dev -g

ts-node-dev 在全局提供了 tsnd 这一简写,你可以运行 tsnd 来检查安装情况。最常见的使用命令是这样的:

ts-node-dev --respawn --transpile-only app.ts

respawn 选项启用了监听重启的能力,而 transpileOnly 提供了更快的编译速度。你可以查看官方仓库来了解更多选项,但在大部分场景中以上这个命令已经足够了。

更方便的类型兼容性检查

某些时候,我们在进行类型比较时,需要使用一个具有具体类型的变量与一个类型进行赋值操作,比如下面这个例子中:

interface Foo {
  name: string;
  age: number;
}

interface Bar {
  name: string;
  job: string;
}

let foo:Foo = {
  name: '林不渡',
  age: 18
}

let bar:Bar = {
  name: '林不渡',
  job: 'fe'
}

foo = bar;

在“只是想要进行类型比较”的前提下,其实并没有必要真的去声明两个变量,即涉及了值空间的操作。我们完全可以只在类型空间中(你可以理解为用于存放 TypeScript 类型信息的内存空间)比较这些类型,只需要使用 declare 关键字:

interface Foo {
  name: string;
  age: number;
}

interface Bar {
  name: string;
  job: string;
}

declare let foo: Foo;
declare let bar: Bar;

foo = bar;

你可以理解为在开始时的例子,我们使用一个值空间存放这个变量具体的属性,一个类型空间存放这个变量的类型。而通过 declare 关键字,我们声明了一个仅在类型空间存在的变量,它在运行时完全不存在,这样就避免了略显繁琐的属性声明。

对于类型兼容的检查,除了两两声明然后进行赋值以外,我们还可以通过工具类型的形式,如 tsd 这个 npm 包提供的一系列工具类型,能帮助你进行声明式的类型检查::

import { expectType } from 'tsd';

expectType<string>("linbudu"); // √
expectType<string>(599); // ×

这一部分的内容并不是初学需要掌握的,但你可以选择提前用起来,不必急着去理解具体的实现原理。

它的结构大致是这样:expectType<你预期的类型>(表达式或变量等),除了 expectType(检查预期类型与表达式或变量的类型是否一致),tsd 还提供了 expectNotType(检查预期类型与表达式或变量的类型是否不同)、expectAssignable(检查表达式或变量的类型是否能赋值给预期类型)等工具类型,其中涉及工具类型与泛型的知识,我们会在后面的课程中一一讲解。

总结

在这一节中,我们主要了解了 TypeScript 开发环境的搭建,包括了 VS Code 的配置、插件,使用 Playground 作为一个简易又强大的临时编辑器,以及如何使用 ts-node 与 ts-node-dev 来快速执行你的 ts 文件。在最后,我们稍微提前了一些对后面学习大有裨益的知识,即通过类型声明(declare)与 tsd 来进行更方便的类型兼容性检查。

这些知识不仅仅只在这本小册的学习过程中起到作用,它们在未来实际项目开发中也是你的得力助手。本着磨刀不误砍柴工的原则,请务必搭建出你最舒适的 TypeScript 开发环境后,再开始这本小册的学习。

扩展阅读

require extension

我们知道,node 中最早使用的是 CommonJs 与 require 来进行模块的导入,除了 .js 文件的导入以外,node 中还支持以扩展的形式来提供自定义扩展名的模块加载机制,这也是 ts-node、require-ts (允许你去 require 一个 TS 文件)这些工具库的工作原理,它们的核心逻辑其实都是通过 require.extension,注册了 .ts 文件的处理逻辑:

require.extenstions['.ts'] = function (module, filename) {
  const content = fs.readFileSync(filename, 'utf8')
  module._compile(content, filename)
}

在 require-ts 中,使用了 pirates 这个库来简化注册逻辑:

const compiler = new Compiler();

addHook(
  (code, filename) => {
    return compiler.compile(filename, code)
  },
  { exts: ['.ts', '.tsx'], matcher: () => true }
)

NodeJs 中的 require 逻辑执行大概是这样的:

  • Resolution,基于入参拼接出 require 文件的绝对路径,当路径中不包含后缀名时,会按照 node 的模块解析策略来进行处理,如 require('./utils') 会解析到 PATH/TO/project/utils.js,而 require('project-utils') 会解析到 PATH/TO/project/node_modules/project-utils/src/index.js,以及内置模块等。需要注意的是在浏览器中,require 需要带上完整的后缀名(浏览器并不能查找服务器的文件),但一般 bundler 会帮你处理好。
  • 基于绝对路径,去 require.cache 这个全局变量中,查找此文件是否已经已缓存,并在存在时直接使用缓存的文件内容(即这个文件的导出信息等)。
  • Loading,基于绝对路径实例化一个 Module 类实例,基于路径后缀名调用内置的处理函数。比如 js、json 文件都是通过 fs.readFileSync 读取文件内容。
  • Wrapping,对于 js 文件,将文件内容字符串外层包裹一个函数,执行这个函数。对于 Json 文件,将内容包裹挂载到 module.exports 下。
  • Evaluating,执行这个文件内容。
  • Caching,对于未曾缓存的文件,将其执行结果缓存起来。

在上述过程中进行操作拦截,就可以实现很多有用的功能。比如对 .ts 文件去注册自定义的处理函数,将其编译为可以直接执行的 js 代码(ts-node/register),对 .js 代码进行预处理(babel-register),在代码执行时进行覆盖率统计(istanbul)。以及,对 require.cache 进行缓存清除来实现 node 服务的热更新(decache),但这里涉及到 require cache 的缓存策略,与本小册的主题没有太大关联,就不做展开啦。

留言
Ctrl + Enter
全部评论(64)
嘎嘎🙊的头像
删除
打卡[力量]
点赞
回复
阿秉秉一直在努力的头像
删除
前端 @ 京东
打卡 真好
点赞
回复
啊逼不懂code的头像
删除
很好,打卡
点赞
回复
YoHo的头像
删除
摸鱼 @ 不到10点不许走
找不到模块“tsd”或其相应的类型声明,我应该怎么处理
点赞
2
删除
在你项目内部装一下这个包就行
点赞
回复
删除
tsd是个npm依赖,你得npm i tsd 装一下,如果安装了,还是不生效你得看报错,可能需要配置下tsconfig.json,加一个键值对"moduleResolution":"node",
点赞
回复
灵心如玉A的头像
删除
Web前端工程师
打卡
点赞
回复
用户2362677620139的头像
删除
打卡
点赞
回复
机智的狮老师的头像
删除
可视化工程师
打卡
点赞
回复
三寸光阴_的头像
删除
cv工程师
打卡
点赞
回复
Enoch__的头像
删除
打卡
点赞
回复
寿司八哥的头像
删除
Web3D @ 图形起源
打卡
点赞
回复
canyon的头像
删除
前端开发 @ 阿里妈妈
为什么要涉及到node的内容[流泪]
点赞
回复
__小东__的头像
删除
前端 @ 360金融
我也来打卡
点赞
回复
学会学会的头像
删除
前端小白
打卡
webstorm 党路过[呲牙]
1
1
删除
刚使用一段时间webstorm 发现node -r ts-node/register index.ts这个不行网上查了一下 不能在全局安装 需要安装在本地
点赞
回复
前端摸鱼儿的头像
删除
--emit 配置了没有效果,项目并没有生成.tsnode文件夹?不渡怎么看?
点赞
5
删除
(作者)
ts-node --emit -H index.ts
点赞
回复
删除
10.4版本 还是没有效果
ts-node --emit -H index.ts
点赞
回复
删除
emit 和 compilerHost同时使用的
ts-node --emit -H index.ts
点赞
回复
删除
完整的试下这个命令呢,我试了在 10.3.0 和 10.9.1 都是正常的,ts-node --emit -H index.ts
emit 和 compilerHost同时使用的
点赞
回复
删除
终于知道了他们和 transpileOnly: true 冲突了
ts-node --emit -H index.ts
点赞
回复
前端摸鱼儿的头像
删除
npx typescript --init
应该改成
npx --package typescript tsc --init
# OR
npx -p typescript tsc --init

tsc是虚拟包,需要指定包名然后在执行tsc
收起
点赞
1
删除
(作者)
已更新
点赞
回复
涛涛_江的头像
删除
打卡
点赞
回复
田八的头像
删除
打卡
点赞
回复
前端踩坑人员的头像
删除
打卡
点赞
回复
我很惊诧的头像
删除
打卡,环境配好了,用时30m
image
3
回复
Ranbogit的头像
删除
前端工程师
ranbo打卡
点赞
回复
hedgehog_boy的头像
删除
打卡
点赞
回复
CoderBin的头像
删除
🏆 CoderBin前端空间栈
打卡[奋斗]
1
3
删除
网吧哥学的挺快
1
回复
删除
你咋在这里,是不是学第二遍了[吃瓜群众]
网吧哥学的挺快
点赞
回复
删除
你想多了这是我能学懂的吗
你咋在这里,是不是学第二遍了[吃瓜群众]
点赞
回复
爱乐的陈的头像
删除
ThoughtWorks
Function Like Return Types,显示推导得到的函数返回值类型;
这个可选配置包括 javascript 和 typescript 两个,建议说明一下,我一开始就配错了没生效。
点赞
回复
Sophora的头像
删除
前端摸鱼砖家
打卡
点赞
回复
枫景的头像
删除
前端工程师 @ 杭州某工地
请问 Inlay Hints怎么修改样式,我的开启之后是黄色背景,巨难看。。。
1
3
删除
点赞
回复
删除
// Theme Colors Override
"workbench.colorCustomizations": {
// Overrides Theme Default Colors for InlayHints feature
"editorInlayHint.background": "#00001CCC",
"editorInlayHint.foreground": "#99FFBBCC",

// Overrides Theme Parameter hints fg for InlayHints feature
"editorInlayHint.parameterBackground": "#00001CCC",
"editorInlayHint.parameterForeground": "#99FFBBCC",

// Overrides Theme Type hints fg for InlayHints feature
"editorInlayHint.typeBackground": "#08000088",
"editorInlayHint.typeForeground": "#DDEEFF88"
},
收起
1
回复
删除
谢谢
点赞
回复
午与羽的头像
删除
Talking @ Imagination.
打卡
点赞
回复
给我一毛钱的头像
删除
前端开发攻城狮
打卡
点赞
回复
掘金黄金双枪的头像
删除
希望以后能有个react hooks ts redux 的实战版[色]
点赞
回复
guaishou的头像
删除
点赞
回复
Big_Rice的头像
删除
前端工程师 @ 未知
打卡
点赞
回复
杨彤杰的头像
删除
typescript Inlay Hints,楼主,你好,这个 配置没找到,是什么原因
点赞
2
删除
(作者)
这个不是一个单独的配置项,而是一类,搜索它出来的配置都属于 inlay hints~
1
回复
删除
好的,谢谢
这个不是一个单独的配置项,而是一类,搜索它出来的配置都属于 inlay hints~
点赞
回复
阳树阳树的头像
删除
蔚来@FE前端
打卡
点赞
回复
南亦的头像
删除
卑微程序员
webstorm 党咋办
3
1
删除
webstorm自带识别以及类型提示展示的,很方便。
点赞
回复
拍照的小盆爸的头像
删除
前端 @ 天房科技
这四个都开了,插件都装了,ts特别好。 想问下作者,在vue文件中,也能这么显示吗,怎么设置下。我现在是鼠标上来,能显示,不知道怎么样也能和ts一样,只直接显示出来。
image
点赞
2
删除
哥们找到vue中提示的方法了吗
点赞
回复
删除
找到了 就是别写在vue文件里面 单独写在js里面再引入就有了
哥们找到vue中提示的方法了吗
点赞
回复
木子烁束岸的头像
删除
点赞
回复
糖瓶的头像
删除
前端工程师 @ 🍬
打卡[奋斗]
点赞
回复
杨永安的头像
删除
webstorm 党路过
点赞
回复
好纠结的头像
删除
打卡
点赞
回复
zhedream的头像
删除
全栈工程师 @ @zhedream
打卡
1
回复
一米八的萝卜的头像
删除
NodeJs 中的 require 逻辑执行大概是这样的:

我记得在引入新模块的时候会先进行缓存处理,这时候的`module.exports`只是一个空对象,后续才进行实例化Module,加载代码包裹等等。先加载再缓存的话 并不能解决循环依赖的问题
点赞
回复
爱吃鱼的桶哥Z的头像
删除
伪 · 全栈打杂攻城狮
打卡学习~
点赞
回复
前端阿鹏的头像
删除
打卡
点赞
回复
codermao的头像
删除
web实习 @ 小红书
打卡
点赞
回复
The action has been successful