50万奖金+官方证书,深圳国际金融科技大赛正式启动,点击报名 了解详情
写点什么

TypeScript 3.0 重磅发布,新特性详解!

  • 2018-08-07
  • 本文字数:7650 字

    阅读完需:约 25 分钟

今天,微软正式发布 TypeScript 3.0,这是 TypeScript 之旅的一个新的里程碑!

3.0 虽然是个大版本,但并没有包含太多重大的变更(也就是说升级很容易)。新版本引入了一种新的灵活且可扩展的方式来构建项目、对操作参数列表提供了更强大的支持、新的强制显式检查类型、更好的 JSX 支持、更好的错误 UX,等等。

项目引用

TypeScript 3.0 引入了“项目引用”这一重大特性,让一个 TypeScript 项目可以依赖于其他 TypeScript 项目——特别是可以让 tsconfig.json 文件引用其他的 tsconfig.json 文件。这样可以更容易地将代码拆分为更小的项目,也意味着可以逐步加快项目的构建速度,并支持跨项目浏览、编辑和重构。

下面是带有项目引用的 tsconfig.json 的示例:

复制代码
// ./src/bar/tsconfig.json
{
"compilerOptions": {
// 用于项目引用
"composite": true,
"declaration": true,
// 其他选项
"outDir": "../../lib/bar",
"strict": true, "module": "esnext", "moduleResolution": "node",
},
"references": [
{ "path": "../foo" }
]
}

references 指定了其他 tsconfig.json 文件(或包含这个文件的文件夹)。每个引用都是一个带有路径字段的对象,TypeScript 知道在构建当前项目时需要首先构建被引用的项目。

composite 字段用于指定启用某些选项,让其他项目可以引用和增量构建这个项目。智能增量式的构建是很重要的,因为构建速度是影响项目的重要因素之一。例如,如果 front-end 依赖 shared,而 shared 依赖 core,项目引用 API 负责检测 core 的变更,并在 core 生成的某些类型文件(即.d.ts 文件)发生变更时重新构建 shared。这意味着对 core 的变更并不会重新构建所有东西。因此,在设置 composite 时也必须设置 declaration。

--build 模式

对于简单的应用程序和库,不需要使用额外的外部工具。因此,tsc 加入了一个新的 --build 选项。

我们可以使用 tsc --build(或 tsc -b)来构建一组项目和它们的依赖项。使用这种新的构建模式时,必须首先设置 --build,并且可以与其他选项配对使用:

  • --verbose:显示构建步骤细节
  • --dry:执行构建而不生成文件
  • --clean:尝试删除生成的文件
  • --force:强制重新构建完整的项目

控制输出结构

如果你有过在客户端和服务器之间共享 TypeScript 代码的经验,那么可能会遇到控制输出结构的问题。

例如,如果 client/index.ts 和 server/index.ts 都引用了以下项目的 shared/index.ts:

那么构建 client 和 server 将会得到:

而不是:

我们最终在 client 和 server 中都生成了一个共享副本。问题在于,TypeScript 会贪婪地查找.ts 文件,并尝试将它们包含在给定的编译中。理想情况下,TypeScript 应该知道这些文件不需要在同一个编译中构建,而是跳转到.d.ts 文件中获取类型信息。

为 shared 创建 tsconfig.json 文件,并使用项目引用就可以解决这个问题。它会告诉 TypeScript:

  1. 应该单独构建 shared
  2. 从../shared 导入时,我们应该在其输出目录中查找.d.ts 文件。

这样可以避免触发双重构建以及意外地引入 shared 的所有内容。

使用元组提取和传递参数列表

在 JavaScript 中,我么可以使用 arguments 或剩余参数(如…rest)来表示参数列表。

复制代码
function call(fn, ...args) {
return fn(...args);
}

call 可用于调用包含任意参数的函数,而不需要像其他语言一样定义 call0、call1、call2 等等。然而,如果不进行函数重载,就很难在 TypeScript 中表达这种静态类型的情况:

复制代码
// TODO (billg): 声明 5 个重载够了吗?
function call<T1, T2, T3, T4, R>(fn: (param1: T1, param2: T2, param3: T3, param4: T4) => R, param1: T1, param2: T2, param3: T3, param4: T4): R
function call<T1, T2, T3, R>(fn: (param1: T1, param2: T2, param3: T3) => R, param1: T1, param2: T2, param3: T3): R
function call<T1, T2, R>(fn: (param1: T1, param2: T2) => R, param1: T1, param2: T2): R
function call<T1, R>(fn: (param1: T1) => R, param1: T1): R;
function call<R>(fn: () => R, param1: T1): R;
function call(fn: (...args: any[]) => any, ...args: any[]) {
return fn(...args);
}

TypeScript 3.0 可以让我们更好地模拟这些场景,就是将剩余参数作为泛型,并将这些泛型推断为元组类型:

复制代码
function call<TS extends any[], R>(fn: (...args: TS) => R, ...args: TS): R {
return fn(...args);
}

在调用 call 函数时,TypeScript 将尝试从传给 fn 的内容中提取参数列表,并将其转换为元组:

复制代码
function foo(x: number, y: string): string {
return (x + y).toLowerCase();
}
 
// `TS` 被推断为 `[number, string]`
call(foo, 100, "hello");

当 TypeScript 将 TS 推断为 [number,string] 时,我们可以重用 call 的剩余参数。

复制代码
function call(fn: (...args: [number, string]) => string, ...args: [number, string]): string

在 TypeScript 3.0 中,剩余参数中的元组会被扁平化,变成没有元组的简单参数:

复制代码
function call(fn: (arg1: number, arg2: string) => string, arg1: number, arg2: string): string

更丰富的元组类型

现在,元组支持尾部可选元素:

复制代码
type Coordinate = [number, number, number?];

其次,元组现在也支持尾部的剩余元素。

复制代码
type OneNumberAndSomeStrings = [number, ...string[]];

当没有其他元素存在时,元组中的剩余元素与其自身相同:

复制代码
type Foo = [...number[]]; // 等同于 `number[]`.

最后,元组现在可以是空的!

复制代码
type EmptyTuple = [];

改进的错误和 UX

社区希望对错误消息进行改进,虽然我们没有全部完成任务,但经过努力,还是在 TypeScript 3.0 中带来了一些改进。

相关错误关联

相关错误关联是向用户显示错误信息的新方法。在 TypeScript 3.0 中,在当前位置可以显示其他位置的消息,以便让用户推断出错误的原因和结果。



在某种意义上说,相关的错误消息不仅可以为用户提供参考,还可以为用户提供面包屑,可以找到出错的位置。

改进的消息和细节

在 3.0 中,我们试图解决一些与错误消息有关的核心问题,提供更智能、更清晰、更准确的错误消息体验。我们的努力也得到了回报,并将提供更短、更清晰的错误消息。



unknown 类型

TypeScript 3.0 引入了一种名为 unknown 的新类型。与 any 一样,可以把任意值赋给 unknown。不过,与 any 不同的是,如果没有使用类型断言,则几乎没有可赋的值。你也不能访问 unknown 的任何属性,或者调用 / 构建它们。

复制代码
let foo: unknown = 10;
// 因为 `foo` 是 `unknown` 类型, 所以这些地方会出错。
foo.y.prop;
foo.z.prop;
foo();
new foo();
upperCase(foo);
foo `hello world!`;
function upperCase(x: string) {
return x.toUpperCase();
}

这个时候,我们可以执行强制检查,或者使用类型断言。

复制代码
let foo: unknown = 10;
 
function hasXYZ(obj: any): obj is { x: any, y: any, z: any } {
return !!obj &&
typeof obj === "object" &&
"x" in obj && "y" in obj && "z" in obj
}
 
// 使用用户定义的类型检查...
if (hasXYZ(foo)) {
// ... 现在可以访问它的属性。
foo.x.prop;
foo.y.prop;
foo.z.prop;
}
 
// 通过使用类型断言,我们告诉 TypeScript,我们知道自己在做什么。
upperCase(foo as string);
 
function upperCase(x: string) {
return x.toUpperCase();
}

支持 JSX 中的 defaultProps

注意:在撰写本文时,React 的.d.ts 文件可能尚不支持此功能。

TypeScript 3.0 在 JSX 命名空间中引入了一个新的类型别名 LibraryManagedAttributes。这是一个辅助类型,用于告诉 TypeScript 某个 JSX 标记可以接受哪些属性。使用这种通用类型,我们可以模拟 React 的特定行为,例如 defaultProps,以及某种程度上的 propTypes。

复制代码
export interface Props {
name: string
}
 
export class Greet extends React.Component<Props> {
render() {
const { name } = this.props;
return <div>Hello ${name.toUpperCase()}!</div>;
}
static defaultProps = { name: "world"}
}
 
// 不需要类型断言!
let el = <Greet />

编辑器改进

导入重构

有时候,指定每一个导入模块的详细路径可能很麻烦,比如:

复制代码
import * as dependency from "./dependency";
 
// 这些都是重复的
 
dependency.foo();
dependency.bar();
dependency.baz();

如果单独导入要使用的东西,到后面可能会搞不清楚它们是来自哪里的,比如:

复制代码
import { foo, bar, baz } from "./dependency";
 
// 在代码文件的很后面...
 
foo();
bar();
baz();

TypeScript 3.0 提供了重构选项,可以轻松实现在上述两种情况之间切换。



JSX 标签自动完成和可折叠轮廓

TypeScript 现在为 JSX 提供了两个新的生产力功能:

  • JSX 标签自动完成
  • JSX 可折叠轮廓

快速清理无法触及的代码和未使用的标签

TypeScript 现在为删除任何无法触及的代码和未使用的标签提供了快速清理的方法。

其他变更

unknown 是保留的类型名称

因为 unknown 是一种新的内置类型,所以它不能再用于类型声明,如接口、类型别名或类。

API 的变更

  • 函数 escapeIdentifier 和 unescapeIdentifier 已被删除。
  • TypeChecker#getSuggestionForNonexistentProperty、TypeChecker#getSuggestionForNonexistentSymbol 和 TypeChecker#getSuggestionForNonexistentModule 方法已经转为私有,不再是公共 API 的一部分,详见#25520( https://github.com/Microsoft/TypeScript/pull/25520 )。

查看英文原文: https://blogs.msdn.microsoft.com/typescript/2018/07/30/announcing-typescript-3-0/#named-import-refactorings

2018-08-07 19:004172
用户头像

发布了 731 篇内容, 共 476.2 次阅读, 收获喜欢 2008 次。

关注

评论 1 条评论

发布
暂无评论
发现更多内容

Go 语言 new 和 make 关键字的区别

AlwaysBeta

面试 Go 语言

复杂「场景」数据导入导出

Java 架构

照片肖像美容软件:ON1 Portrait AI 2023.1 激活版

真大的脸盆

Mac Mac 软件 照片处理工具 肖像美容软件

面试突击:MVCC 和间隙锁有什么区别?

王磊

java面试

通过 Amazon Managed Microsoft Active Directory 运行混合 Active Directory 服务

亚马逊云科技 (Amazon Web Services)

Amazon

对于Vue3和Ts的心得和思考

京东科技开发者

Vue ts 京东云 京东技术 企业号 3 月 PK 榜

PyTorch 深度学习实战 | 知识图谱嵌入结合图路径的推荐 RippleNet

TiAmo

深度学习 Ripplenet

Spring中PropertyPlaceholderConfigurer替换占位符的问题

石臻臻的杂货铺

spring-boot

华为P系列,雪夜梅花开,似是故人来

脑极体

华为

分享一个修改了xml文件再也不用重启的项目mybatis-xmlrealod

越长大越悲伤

Java 开源 Spring Boot mybatis 开源项目

有一种焦虑叫ChatGPT(62/100)

hackstoic

AI AIGC ChatGPT

Spring源码核心剖析

京东科技开发者

spring 前端 京东云 京东技术 企业号 3 月 PK 榜

【JVM盲点补漏系列】「并发编程的难题和挑战」深入理解JMM及JVM内存模型知识体系机制(1)

码界西柚

Java JVM 3月月更

机器学习算法(六)基于天气数据集的XGBoost分类预测

汀丶人工智能

数据挖掘 数据分析 XGBoost

狼来了

俞凡

人工智能

Gestures(手势)

芯动大师

Activity 手势 gesturelistener

面试官:如何保证接口幂等性?一口气说了9种方法!

程序员大彬

Java 接口幂等

电商秒杀系统

Geek_7d539e

湃方科技联合CnosDB助力旋转类机械预测性维护

CnosDB

数据库 开源 时序数据库 CnosDB

“找工难&金三银四”,关于面试,你需要知道的这些事

IT学习日记

面试 Java、 找工作 简历模板

初识 Flutter 的绘图组件 — CustomPaint

岛上码农

flutter ios 安卓 移动端开发 跨平台开发

三天吃透MySQL面试八股文

程序员大彬

MySQL java面试

软件测试/测试开发丨app自动化之如何参数化用例

测试人

软件测试 自动化测试 测试开发

别急着给中国版ChatGPT唱赞歌:“追风者”无缘“星辰大海”

Alter

百度 大模型 ChatGPT 文心一言

设计模式之美––依赖反转

GalaxyCreater

设计模式

SaaS 软件搞定客户的关键环节分析

产品海豚湾

产品经理 SaaS 产品运营 B 端产品 互联网产品

软件测试/测试开发丨app自动化测试之Andriod WebView如何测试

测试人

软件测试 自动化测试 测试开发

adb的常用命令

测吧(北京)科技有限公司

测试

如何编写测试用例?

测吧(北京)科技有限公司

测试

隐式等待、显示等待和强制等待

测吧(北京)科技有限公司

测试

华为都入场做ERP了,国产ERP困局如何破解?

陈飞

分布式数据库 国产化 ERP SaaS 平台 PaaS平台

TypeScript 3.0重磅发布,新特性详解!_微软_MSDN_InfoQ精选文章