写点什么

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:004009
用户头像

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

关注

评论 1 条评论

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

MySQL中支持的字符集和排序规则

@下一站

MySQL 技术 字符集 11月月更

小令动态 | 令牌云成功通过国家等保三级认证

令牌云数字身份

【Ajax】全面了解http协议

坚毅的小解同志

HTTP 11月月更

SAP UI5 应用和 Angular 应用视图里控件 id 生成逻辑的异同比较

汪子熙

前端开发 angular SAP SAP UI5 11月月更

一周活动速递|Paper Time第五期;技术征文大赛即将收官

OceanBase 数据库

HashData携手恒丰银行 入选信通院金融科技创新应用五大“最受关注案例”

酷克数据HashData

云数据仓库

小令动态 | 令牌云新获上海市创新资金立项支持,此前还有......

令牌云数字身份

【原生Ajax】全面了解xhr的概念与使用。

坚毅的小解同志

ajax 11月月更

云计算:基于互联网的超级计算

Finovy Cloud

云计算 云渲染

【Ajax】如何通过axios发起Ajax请求

坚毅的小解同志

ajax 11月月更

旺链科技肖慧荣登超级账本2022年第三季度中国贡献榜

旺链科技

区块链 产业区块链 VoneBaaS 企业号十月PK榜

华为云虚拟专用网络VPN,为企业铺就数据上云的安全路

路过的憨憨

百度APP iOS端内存优化实践-内存管控方案

百度Geek说

android 后端 内存管理 企业号十月 PK 榜

OpenHarmony 3.2 Beta多媒体系列——音视频播放框架

OpenHarmony开发者

OpenHarmony

LR低代码快速开发平台 高效调整企业组织架构

力软低代码开发平台

小令观点 | 从大批QQ账号被盗,看账号安全与数据资产问题

令牌云数字身份

数据安全 账号安全

小令观点丨现代版 “见令如见人”

令牌云数字身份

新版阿里神级“高并发”教程《基础+实战+源码+面试+架构》

钟奕礼

Java java面试 java编程 程序员、

嘉为科技彭一宽:组织度量,先做造钟人,再做报时人

嘉为蓝鲸

DevOps 度量

“如何实现高效的应用交付”鲁班会开发者训练营厦门站进行时

华为云开发者联盟

云计算 软件开发 华为云 应用集成

一个宁静祥和没有bug的下午和SqlSession的故事

京东科技开发者

mybatis sql Spring Boot CLI SQLSession

【Ajax进阶】跨域和JSONP的学习

坚毅的小解同志

ajax 11月月更

4.0体验站|OceanBase 4.0 我回来给你点个赞

OceanBase 数据库

JS有哪些变态语法,你知道吗?

千锋IT教育

【Ajax】如何通过axios发起Ajax请求

坚毅的小解同志

ajax 11月月更

【jquery ajax】实现文件上传提交

坚毅的小解同志

ajax 11月月更

字节跳动基于数据湖技术的近实时场景实践

字节跳动数据平台

数据湖 火山引擎

python常用的内置对象

乔乔

11月月更

Bigkey问题的解决思路与方式探索

vivo互联网技术

redis dba bigkey

数维图智慧化工园区三维系统 助力危化企业安全生产

2D3D前端可视化开发

物联网 数据可视化 数字孪生 智慧化工园区 化工园区三维可视化

Mybatis中使用${}和使用#{}

@下一站

mybatis MyBatis标签 Java core 11月月更

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