找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2189

积分

0

好友

306

主题
发表于 昨天 01:26 | 查看: 6| 回复: 0

TypeScript 编辑器类型推断示例

显式类型

在前面的例子中,我们没有指定 persondate 的类型。现在,我们来修改代码,明确告知 TypeScript:person 是一个字符串,date 是一个 Date 对象。同时,我们还将调用 datetoDateString() 方法。

function greet(person: string, date: Date) {
  console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}

我们在 persondate 参数后面添加了类型注解,用于描述调用 greet 时可以传入的值类型。你可以这样解读函数签名:greet 接收一个 string 类型的 person 和一个 Date 类型的 date

添加类型注解后,TypeScript 就能帮助我们发现对 greet 函数的错误调用。例如:

function greet(person: string, date: Date) {
  console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}

greet("Maddison", Date());
// 错误:类型“string”的参数不能赋给类型“Date”的参数。

等等,为什么第二个参数会报错?

这可能会让不少人感到意外:在 JavaScript 中,直接调用 Date()(不加 new)返回的是一个字符串。而我们期望的是一个 Date 对象,因此需要使用 new Date() 来构造:

greet("Maddison", new Date());

修复后的代码如下:

function greet(person: string, date: Date) {
  console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}

greet("Maddison", new Date());

需要强调的是,我们并不总是需要显式地编写类型注解。在许多情况下,即使省略类型,TypeScript 也可以自动推断出变量的类型。

在上面的图片示例中,尽管我们没有告诉 TypeScript msgstring 类型,它依然成功地推断了出来。这恰好是 TypeScript 的一项优势——如果类型系统能够自动推断,通常就不需要手动添加注解。

[!note]
上图中的提示气泡模拟了在编辑器中将鼠标悬停在变量上时,TypeScript 提供的类型推断信息。

类型擦除

让我们看看用 tsc 编译上述 greet 函数后,会生成怎样的 JavaScript 代码:

"use strict";
function greet(person, date) {
    console.log("Hello ".concat(person, ", today is ").concat(date.toDateString(), "!"));
}
greet("Maddison", new Date());

请注意两个关键点:

  1. persondate 参数上的类型注解完全消失了
  2. 使用反引号编写的模板字符串被转换成了普通的字符串拼接。

第二点我们稍后讨论,先聚焦第一点:类型注解在输出的 JavaScript 中被彻底移除了。因为类型注解并非 JavaScript(更准确地说,是 ECMAScript)的语法,所以没有任何浏览器或运行时能够直接执行未处理的 TypeScript 代码。

这也正是 TypeScript 需要编译器的根本原因——它必须移除或转换所有 TypeScript 特有的语法,生成标准的 JavaScript 代码才能运行。大多数 TypeScript 语法,包括类型注解,都会在编译阶段被“擦除”。

[!note]
请记住:类型注解不会改变程序的运行时行为。

降级编译

在上面的编译结果中,另一个明显的变化是模板字符串被重写了。原始代码是:

`Hello ${person}, today is ${date.toDateString()}!`;

编译输出变成了:

"Hello ".concat(person, ", today is ").concat(date.toDateString(), "!");

为什么会这样?

这是因为模板字符串是 ECMAScript 2015(也称为 ES6)引入的新特性。TypeScript 能够将较新版本 ECMAScript 的代码“降级”编译为较旧的版本,例如 ES3 或 ES5。这个过程被称为降级编译

TypeScript 默认的编译目标是 ES5,这是一个相对早期的 ECMAScript 版本。如果我们希望生成更现代的代码,可以通过设置 target 编译选项来选择更高的目标版本。例如,运行以下命令:

tsc --target es2015 hello.ts

这将把编译目标设置为 ECMAScript 2015,意味着生成的代码会保留模板字符串等 ES2015 特性(假设运行环境支持)。运行上述命令后,输出如下:

function greet(person, date) {
  console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}

greet("Maddison", new Date());

[!note]
虽然默认目标是 ES5,但目前绝大多数浏览器都已支持 ES2015。因此,除非你需要兼容非常老旧的浏览器,大多数开发者都可以安全地将编译目标设置为 ES2015 或更高版本。

严格模式

不同的开发者对 TypeScript 类型检查的严格程度有不同的需求。

有些人更喜欢一种宽松、可选的方式,只对程序的部分区域进行类型验证,同时享受良好的工具支持。这也是 TypeScript 的默认体验——类型是可选的,推断尽可能宽松,并且不会对可能为 nullundefined 的值进行严格检查。就像 tsc 报错时仍然会生成输出一样,这种默认设置旨在“尽量不打扰你”。对于迁移现有的 JavaScript 项目,这种宽松的方式是一个很好的起点。

相反,许多开发者希望 TypeScript 尽可能严格地验证代码。为此,TypeScript 提供了一系列严格模式配置选项。

这些配置项将静态类型检查从一个简单的“开关”转变为一个可调节的“刻度盘”。你“拧得越紧”,TypeScript 检查得就越严格。虽然这可能会增加初期的工作量,但从长远来看通常是值得的,它能发现更多潜在问题,并提供更强大的工具支持。对于新项目,强烈建议启用所有严格模式选项。

TypeScript 提供了多个可以单独开启或关闭的类型检查严格性标志。除非特别说明,本手册中的所有示例都将在启用所有严格模式的前提下进行。你可以通过命令行参数 --strict,或在 tsconfig.json 中配置来一次性启用所有选项:

{
  "compilerOptions": {
    "strict": true
  }
}

当然,你也可以根据需求单独关闭某些检查。

其中最重要的两个严格选项是:

  • noImplicitAny:禁止隐式的 any 类型
  • strictNullChecks:严格的空值检查(控制 nullundefined

noImplicitAny

如前所述,在某些情况下,TypeScript 不会主动推断具体类型,而是回退到最宽松的类型:any。这本身并不一定是坏事——使用 any 本质上就是回归到原生 JavaScript 的开发体验。

但问题在于,频繁使用 any 会削弱 TypeScript 的价值。你的程序类型信息越完整,类型系统提供的验证和工具支持就越强大,编码过程中遇到的错误也就越少。

启用 noImplicitAny 编译选项后,TypeScript 会对所有被隐式推断为 any 的变量抛出错误,帮助你及早发现类型缺失的问题。

strictNullChecks

默认情况下,nullundefined 可以被赋值给任何其他类型。这在某些场景下让编码更便捷,但忘记处理 nullundefined 是现实世界中无数 Bug 的根源——甚至被称为“价值十亿美元的错误”。

启用 strictNullChecks 后,TypeScript 会要求你显式地处理 nullundefined,从类型层面强制你考虑这些情况,从而避免遗漏相关判断,减少由空值引发的潜在问题。

译者解读

本篇是 TypeScript 官方手册《基础知识》的下半部分内容。

下半部分继续介绍了 TypeScript 的几个核心基本特征:

  1. 类型注解:如何显式地为变量和参数指定类型。
  2. 类型擦除:编译后类型信息会被移除,不影响运行时。
  3. 降级编译:可以灵活选择编译生成的 JavaScript 目标版本。
  4. 严格模式:通过配置开启更严格的类型检查,提升代码质量。

其中有几点值得特别注意:

  1. 按照官方的建议,实际上不推荐过度使用显式类型注解,更推荐充分利用 TypeScript 强大的类型推断能力。
  2. 如果是全新的项目,建议直接在配置中设置 "strict": true 来启用所有严格模式选项。尽管在编写代码时可能会感觉约束更多,但这能极大地提升代码的健壮性和可维护性。这部分关于工程化配置的知识,可以在前端框架/工程化板块找到更多深入讨论。
  3. 如果是对一个已有的 JavaScript 代码库升级为 TypeScript,那么可以采用渐进式策略,逐步开启严格模式的各个开关。

希望这篇对 TypeScript 官方手册的解读能帮助你更好地理解这门语言的基础。如果你想了解更多关于 JavaScript 和 TypeScript 的核心概念,例如异步编程或事件循环,可以访问云栈社区的 HTML/CSS/JS 板块获取更多学习资源。




上一篇:Java BeanUtils改造:优雅实现List集合数据拷贝
下一篇:C++ std::enable_if_t与SFINAE实战:3大使用场景与代码详解
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-14 17:09 , Processed in 0.206121 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表