报名「音视频前端技术创新实践专场」,听腾讯云技术专家揭秘音视频相关技术>> 了解详情
写点什么

TypeScript 3.6 正式发布!新增 4 项突破性特点

  • 2019 年 9 月 03 日
  • 本文字数:6151 字

    阅读完需:约 20 分钟

TypeScript 3.6正式发布!新增4项突破性特点

8 月 28 日微软正式发布了 TypeScript 3.6 版,本文将主要介绍 TypeScript 3.6 版中的更新内容,包括:语言和编译器、新的 TypeScript Playground、编辑功能等。​


我们很高兴地宣布,TypeScript 3.6 正式面世了!


有些人可能还不太了解 TypeScript,这里做一个简单介绍:TypeScript 是一种基于 JavaScript 的语言,在后者的基础上添加了可选的静态类型;TypeScript 编译器可以检查这些类型以捕获程序中的常见错误(例如属性拼写错误和函数调用错误等);然后可以使用 TypeScript 编译器和 Babel 等工具将基于最前沿规范编写的 TypeScript 代码转换为符合标准的 ECMAScript 代码,后者可在任何浏览器或运行时(甚至是只支持 ES3 或 ES5 的旧版本)上运行。


TypeScript 不仅具备类型检查和较新的 ECMAScript 功能,其编辑工具也是业界一流水平,是 TypeScript 项目不可或缺的一部分;多种类型的编辑器为 TypeScript 提供了代码自动完成、重构和快速修复等功能。如果你在 Visual Studio 或 Visual Studio Code 中编辑过 JavaScript 文件,其实这些体验是由 TypeScript 提供的,所以你可能已经在不知情的情况下开始使用 TypeScript 了!


你可以查看TypeScript官方网站了解更多信息。TypeScript 可以通过NuGet获取,或在 npm 中键入以下命令:


npm install -g typescript
复制代码


可选的编辑器有:



不久的将来我们还会提供其他编辑器的支持


下面来看看 3.6 版中的更新内容吧!


语言和编译器

更严格的生成器

TypeScript 3.6 对迭代器和生成器函数的检查更严格了。在早期版本中,用户使用生成器时无法判断一个值是从生成器 yield 还是返回的。


function* foo() {    if (Math.random() < 0.5) yield 100;    return "Finished!"}
let iter = foo();let curr = iter.next();if (curr.done) { // TypeScript 3.5及之前版本把它当成 'string | number'. // 它应该知道这是 'string' ,因为 'done' 是 'true'! curr.value}
复制代码


此外,生成器之前会假定 yield 的类型总是 any。


function* bar() {    let x: { hello(): void } = yield;    x.hello();}
let iter = bar();iter.next();iter.next(123); // oops! runtime error!
复制代码


TypeScript 3.6 中的检查器现在知道第一段代码中的 curr.value 的正确类型应该是 string,并且在第二段代码中调用 next()时会正确地报错。这是因为 Iterator 和 IteratorResult 类型声明中现在引入了一些新的类型参数,且新版 TypeScript 会用 Generator 这个新类型来表示生成器。


Iterator 类型现在允许用户指定 yield 类型、返回的类型以及 next 可以接受的类型。


interface Iterator<T, TReturn = any, TNext = undefined> {    // 接收 0 或 1 参数 - 不接收 'undefined'    next(...args: [] | [TNext]): IteratorResult<T, TReturn>;    return?(value?: TReturn): IteratorResult<T, TReturn>;    throw?(e?: any): IteratorResult<T, TReturn>;}
复制代码


在此基础上,新的 Generator 类型是一个 Iterator,它总是同时存在 return 和 throw 方法,并且也是可迭代的。


interface Generator<T = unknown, TReturn = any, TNext = unknown>        extends Iterator<T, TReturn, TNext> {    next(...args: [] | [TNext]): IteratorResult<T, TReturn>;    return(value: TReturn): IteratorResult<T, TReturn>;    throw(e: any): IteratorResult<T, TReturn>;    <a href="">Symbol.iterator: Generator<T, TReturn, TNext>;}</a href="">
复制代码


为了区分返回值和生成值,TypeScript 3.6 将 IteratorResult 类型转换为差别联合类型:


type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
interface IteratorYieldResult<TYield> { done?: false; value: TYield;}
interface IteratorReturnResult<TReturn> { done: true; value: TReturn;}
复制代码


简而言之,这意味着你在直接处理迭代器时能够适当地缩小迭代器的值。


为了正确表示可以从调用 next()传递给生成器的类型,TypeScript 3.6 还可以在生成器函数主体内推断出某些 yield 用法。


function* foo() {    let x: string = yield;    console.log(x.toUpperCase());}
let x = foo();x.next(); // 对 'next' 的第一个调用都会被忽略x.next(42); // error! 'number' 无法被分配给 'string'
复制代码


如果你更喜欢显式方法,那么还可以强制可以用 yield 表达式执行返回、yield 和计算的值的类型使用显式返回类型。下面的例子中,next( )只能用布尔值调用,并且根据 done 的值,value 可以是 string 或 number。


/** * - yields numbers * - returns strings * - can be passed in booleans */function* counter(): Generator<number, string, boolean> {    let i = 0;    while (true) {        if (yield i++) {            break;        }    }    return "done!";}
var iter = counter();var curr = iter.next()while (!curr.done) { console.log(curr.value); curr = iter.next(curr.value === 5)}console.log(curr.value.toUpperCase());
// prints://// 0// 1// 2// 3// 4// 5// DONE!
复制代码


这部分更改的详情可参阅github


更准确的数组扩展

在目标为 ES2015 之前的版本中,诸如 for/of 循环和数组扩展之类的结构用的发射有些复杂。因此 TypeScript 默认使用更简单的发射,只支持数组类型,并支持使用–downlevelIteration 标志在其他类型上迭代。在此标志下发出的代码更准确,但体积也大得多。


默认情况下–downlevelIteration 是关闭的,因为以 ES5 为目标的用户多数只使用带数组的迭代结构。但某些边缘情况下只支持数组的发射还是有一些可见的差异。


例如,以下示例


[...Array(5)]
复制代码


等同于下列数组:


[undefined, undefined, undefined, undefined, undefined]
复制代码


但是,TypeScript 会将原始代码转换为下面的代码:


Array(5).slice();
复制代码


它们是有些不一样的。Array(5)生成一个长度为 5 的数组,但没有定义的属性槽!


1 in [undefined, undefined, undefined] // true1 in Array(3) // false
复制代码


当 TypeScript 调用 slice()时,它还会创建一个其索引尚未设置的数组。


这可能不太好理解,但其实有许多用户遇到了这种麻烦。TypeScript 3.6 不再使用 slice()和内置函数,而是引入了一个新的__spreadArrays助手,将 ECMAScript 2015 的内容在较旧的目标中模拟出来,不用 --downlevelIteration。 __spreadArrays 也可以在tslib中使用(如果你想缩小包的体积,那么非常值得一试)。


有关更多信息,请参阅资料


改进了 Promise 相关的用户体验

Promise 是当今最常用的异步数据方法之一。然而面向 Promise 的 API 通常会让用户感到困惑。TypeScript 3.6 引入了一些改进,避免用户错误处理 Promise。


例如,在将 Promise 的内容传递给另一个函数之前,人们往往会忘记.then()或 await 这部分内容。TypeScript 现在有了对应的错误消息,并告知用户他们可能应该使用 await 关键字。


interface User {    name: string;    age: number;    location: string;}
declare function getUserData(): Promise<User>;declare function displayUser(user: User): void;
async function f() { displayUser(getUserData());// ~~~~~~~~~~~~~// 类型 'Promise<User>' 的参数不能分配给类型 'User'的参数.// ...// Did you forget to use 'await'?}
复制代码


在 await 或.then()一个 Promise 之前就尝试访问方法也是很常见的错误。这类错误很多,我们都做了改进。


async function getCuteAnimals() {    fetch("https://reddit.com/r/aww.json")        .json()    //   ~~~~    // 属性 'json' 在类型 'Promise<Response>'上不存在.    //    // Did you forget to use 'await'?}
复制代码


这里的目的是就算用户没意识到要 await,起码这些消息能给出一些提示。


除了改进 Promise 相关的错误消息外,我们现在还在某些情况下提供了快速修复。



有关更多详细信息,请参阅原始问题及相关链接。


对标识符的 Unicode 支持改进

当发射到 ES2015 及更高版本的目标时,TypeScript 3.6 对标识符的 Unicode 字符提供了更好的支持。


const 𝓱𝓮𝓵𝓵𝓸 = "world"; // 以前不允许, 现在对 '--target es2015' 允许
复制代码


SystemJS 中的 import.meta 支持

当 module 目标设置为 system 时,TypeScript 3.6 支持将 import.meta 转换为 context.meta。


// 这个模块:
console.log(import.meta.url)
// 被改成:
System.register([], function (exports, context) { return { setters: [], execute: function () { console.log(context.meta.url); } };});
复制代码


在环境上下文中允许 get 和 set 访问器

以前的 TypeScript 版本不允许在环境上下文中 get 和 set 访问器(例如在 declare-d 类中,或者在.d.ts 文件中)。理由是对于这些属性的读写而言访问器与属性没有区别;但是因为 ECMAScript 的类字段提案可能与现有 TypeScript 版本中的行为不同,我们意识到我们需要一种方法来对接这种行为差异,以便在子类中提供适当的错误。


因此,用户现在可以在 TypeScript 3.6 的环境上下文中编写 getter 和 setter。


declare class Foo {    // 在 3.6+ 版本中允许.    get x(): number;    set x(val: number): void;}
复制代码


在 TypeScript 3.7 中编译器也将利用此功能,以便生成的.d.ts 文件也发射 get/set 访问器。


环境类和函数可以合并

在以前版本的 TypeScript 中,任何情况下合并类和函数都是错误的。现在环境类和函数(具有 declare 修饰符或在.d.ts 文件中的类/函数)可以合并了。这意味着现在你可以编写以下代码:


export declare function Point2D(x: number, y: number): Point2D;export declare class Point2D {    x: number;    y: number;    constructor(x: number, y: number);}
复制代码


这样就用不着再写成:


export interface Point2D {    x: number;    y: number;}export declare var Point2D: {    (x: number, y: number): Point2D;    new (x: number, y: number): Point2D;}
复制代码


这样做的一个优点是可以很容易地表达可调用的构造函数模式,同时还允许名称空间与这些声明合并(因为 var 声明不能与 namespace 合并)。


在 TypeScript 3.7 中,编译器也将利用此功能,以便从.js 文件生成的.d.ts 文件可以正确捕获类函数的可调用性和可构造性。


更多详细信息请参阅 GitHub 上的原始PR


为–build 和–incremental 提供 API 支持

TypeScript 3.0 开始支持引用其他项目,并使用–build 标志以增量方式构建它们。之后 TypeScript 3.4 引入了–incremental 标志来保存之前编译的相关信息,这样就可以只重建特定文件了。这些标志可以帮助用户更快、灵活地构建项目。可惜这些标志无法用于 Gulp 和 Webpack 这样的第三方构建工具。TypeScript 3.6 现在公开了两组 API 来处理项目引用和增量程序构建。


创建–incremental 构建时用户可以利用 createIncrementalProgram 和 createIncrementalCompilerHost API。用户还可以使用新公开的 readBuilderProgram 函数从这个 API 生成的.tsbuildinfo 文件中重新保存旧程序实例,该函数仅用于创建新程序(你无法修改返回的实例,它仅用于其他 create*Program 函数中的 oldProgram 参数)。


针对项目引用方面,新版引入了一个新的 createSolutionBuilder 函数,它返回一个新类型 SolutionBuilder 的实例。


有关这些 API 的详细信息可参阅资料


新的 TypeScript Playground

应用户呼吁,新版 TypeScript Playground 做了大幅改进,引入了许多全新功能。新版 Playground 基本上是社区流行的Artem TyurinTypeScript Playground的一个 fork。我们在这里非常感谢 Artem 提供的帮助!


新版 Playground 提供了许多新选项,包括:


  • target 选项(允许用户从 es5 切换到 es3、es2015、esnext 等)

  • 所有严格标志(包括 strict)

  • 支持纯 JavaScript 文件(使用 allowJS 和可选的 checkJs)


这些选项在共享 Playground 样本时也能使用,让用户可以更可靠地共享示例,无需告诉收件人“哦,别忘了打开 noImplicitAny 选项!”。


在不久的将来,我们将改进 Playground 样本功能、添加 JSX 支持、改进自动类型获取等,让用户使用 Playground 时如同在使用自己的编辑器一样。


我们欢迎大家在GitHub上提交反馈和请求!


编辑功能

分号感知代码编辑

Visual Studio 和 Visual Studio Code 等编辑器可以自动应用快速修复、重构、自动从其他模块导入值等转换。这些转换由 TypeScript 提供支持,而旧版本的 TypeScript 会无条件地在每个语句的末尾添加分号;但很多用户不喜欢这种风格,不希望编辑器自动插入分号。


TypeScript 现在变得非常聪明,可以在应用这些编辑时检测你的文件是否使用分号。如果你的文件不怎么用分号的话 TypeScript 也不会添加分号。


更多详细信息请参阅资料


更智能的自动导入

JavaScript 有许多不同的模块语法或约定:ECMAScript 标准是一种、Node 支持的一种(CommonJS)、AMD 一种、System.js 又是一种,还有更多!在大多数情况下 TypeScript 默认使用 ECMAScript 模块语法来自动导入,遇到有些使用不同编译器设置的 TypeScript 项目就不怎么合适,在使用纯 JavaScript 和 require 调用的 Node 项目中也不搭配。


TypeScript 3.6 变得更聪明了一些,可以先查看你现有的导入后再决定怎样自动导入其他模块。你可以在此处查看更多信息


重大更新

命名为“constructor”的类成员现在是构造函数

根据 ECMAScript 规范,使用名为 constructor 的方法的类声明现在是构造函数,无论它们是使用标识符名称还是字符串名称声明都是如此。


class C {    "constructor"() {        console.log("我是构造函数.");    }}
复制代码


注意有一个例外,就是计算属性的名称等于“constructor”的情况。


class D {    <a href="">"constructor" {        console.log("我不是一个构造函数 - 只是一个方法");    }}</a href="">
复制代码


DOM 更新

新版 lib.dom.d.ts 中的许多声明已被删除或更改。具体包括(但不限于)以下内容:


  • 全局 window 不再定义为类型 Window——而是将其定义为类型 Window&typeof globalThis。在某些情况下,最好将其类型称为 typeofwindow。

  • GlobalFetch 已经移除了,取而代之的是 WindowOrWorkerGlobalScope。

  • Navigator 上的某些非标准属性消失了。

  • experimental-webgl 上下文移除了。替代品是 webgl 或 webgl2。


JSDoc 注释不再合并

在 JavaScript 文件中,TypeScript 只会在 JSDoc 注释之前确定声明的类型。


/** * @param {string} arg *//** * oh, hi, were you trying to type something? */function whoWritesFunctionsLikeThis(arg) {    // 'arg' has type 'any'}
复制代码


关键字不能包含转义序列

以前关键字允许包含转义序列。TypeScript 3.6 中就不行了。


while (true) {    \u0063ontinue;//  ~~~~~~~~~~~~~//  error! Keywords cannot contain escape characters.}
复制代码


未来计划

想要了解官方团队未来要开展的工作,请查看今年 7 月至 12 月的半年路线图计划


我们希望这个新版本能继续改善你的开发体验。你有任何建议或遇到任何问题我们都很感兴趣,欢迎大家在GitHub上提交反馈。


英文原文:https://devblogs.microsoft.com/typescript/announcing-typescript-3-6/


2019 年 9 月 03 日 08:205578

评论

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

为什么5、6月会空缺出大量技术岗?跳槽注意事项+面试官关心什么

Java架构师迁哥

百度 Serverless 架构揭秘与应用实践

百度开发者中心

百度 开源 Serverless 云原生

华为HMS生态和1+8+N的交叉点,点透棋局的华为帐号

脑极体

每个开发人员都应该知道的 10 个 GitHub 仓库

LeanCloud

GitHub web开发

仰望天空,脚踏实地 —— CODING OKR 全新上线

CODING DevOps

团队管理 OKR

推荐计划 | 推荐好友用 CODING,获高额返现奖励

CODING DevOps

团队管理 敏捷开发 研发工具 开发团队

参与 Apache 顶级开源项目的 N 种方式,Apache Dubbo Samples SIG 成立!

阿里巴巴云原生

开源 开发者 云原生 dubbo 中间件

部署混合云环境的5大挑战

浪潮云

云计算

论:在996的天下,Java程序员该如何高效学习

Java架构师迁哥

字节跳动Java岗一二三面全经过分享

北游学Java

Java 字节跳动 面试

玩转直播系列之消息模块演进(3)

vivo互联网技术

Java 服务器 消息系统 直播技术

5分钟速读之Rust权威指南(八)

码生笔谈

rust

Hive窗口函数与分析函数

大数据技术指南

hive 5月日更

做一次黑客,入侵一次服务器

叫我阿柒啊

Docker 入侵 docker远程 redis注入

简单了解 MySQL 中相关的锁

SH的全栈笔记

MySQL 后端

ETL-KETTLE工具使用

this

Java 数据 数据同步 ETL

程序员写好技术文章的几点小技巧

阿里巴巴云原生

程序员 云原生 写作 写作技巧

源码不止Spring!阿里殿堂级SSM源码全解小册全网首次公开,原来看源码如此简单!

程序员小毕

Java spring 源码 程序员 面试

首届HarmonyOS开发者创新大赛颁奖典礼于深圳召开

科技汇

Kubernetes 普及系列:容器基础入门

CODING DevOps

Kubernetes

飞猪基于 Serverless 的云+端实践与思考

阿里巴巴云原生

Serverless 容器 运维 云原生 监控

☕【JVM 技术之旅】让你完全攻克内存溢出(OOM)这一难题(上)

浩宇天尚

JVM OOM 异常 Exception 5月日更

超级详细!全网独家首发的SpringCloud Alibaba 到底有多强?

Java 程序员 架构 面试

双非渣硕也能上岸?痛述阿里Java岗7轮技术面经历,险幸上岸

Java 程序员 架构 面试

阿里大牛耗时三年整理出来的4588页Java面试诛仙手册,已全面开源

马小晴

Java 编程 程序员 面试 计算机

存储技术入门,这些硬件应该首先有所了解

SunnyZhang的IT世界

服务器 存储 交换机 san

我粉了!阿里大牛从内部带出来的百亿级高并发系统,从基础到实战、面面俱到

Java 程序员 架构 面试

阿里云原生开源大家族加入中科院软件所开源软件供应链点亮计 - 暑期 2021

阿里巴巴云原生

开源 容器 微服务 开发者 云原生

面向WEB开发人员的Docker(五):部署开发WordPress

devpoint

Docker Wordpress 部署 Docker-compose

高级软件工程师必备的五大技能

架构精进之路

5月日更

IO

ltc

IO流

TypeScript 3.6正式发布!新增4项突破性特点-InfoQ