写点什么

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

  • 2019-09-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-09-03 08:206140

评论

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

WordPress 版本更新

海拥(haiyong.site)

WordPress 6月月更

使用 KubeKey 搭建 Kubernetes/KubeSphere 环境的“心路(累)历程“

胡说云原生

Kubernetes KubeSphere KubeKey

Plugsched 实战解读:如何在不中断业务时对 Linux 内核调度器热升级? | 龙蜥技术

OpenAnolis小助手

Linux 开源 内核 调度 Plugsched

Java培训多线程+List分段解决批量更新太慢

@零度

List 多线程 JAVA开发

Linux开发_网络编程、网络通信介绍

DS小龙哥

6月月更

版式设计三大原则

源字节1号

软件开发 小程序开发

明道云上榜2022年中国信创行业办公软件排行榜

明道云

苹果称M2比intel i5强26倍 虚假营销的实情揭晓!

科技之家

使用 ViroReact 开发增强实现应用的一个具体例子

汪子熙

AR React 增强现实 6月月更

【Python技能树共建】lambda 表达式

梦想橡皮擦

6月月更

JavaScript寄生式组合继承

大熊G

JavaScript 前端 6月月更

Flink CDC + Hudi 海量数据入湖在顺丰的实践

Apache Flink

大数据 flink 编程 流计算 实时计算

Nacos配置中心实战,盘古微服务开发标配组件

码农大熊

微服务架构 nacos 盘古开发框架 分布式开发

物联网低代码平台如何使用操作日志?

AIRIOT

物联网 低代码开发 低代码平台 物联网关

Linux中有趣的命令:cowsay,会说话的牛!

wljslmz

Linux 6月月更 cowsay

【高并发】彻底理解Nginx限流机制与实战

冰河

并发编程 多线程 高并发 异步编程 6月月更

Web Service进阶(八)BASE64Decoder小解

No Silver Bullet

6月月更 BASE64Decoder

Android 产生ANR后的Trace文件的解析

北洋

android 6月月更

关于在线帮助中心你需要思考以下几个问题

小炮

开发一个软件应用程序需要多少钱?

开源直播系统源码

软件开发 定制开发 直播源码

【直播回顾】Hello HarmonyOS应用篇第六课——短视频应用开发

HarmonyOS开发者

HarmonyOS

柴云鹏:创新能力的培养至关重要|OceanBase 数据库大赛访谈

OceanBase 数据库

oceanbase 数据库大赛

spring4.1.8初始化源码学习三部曲之三:AbstractApplicationContext.refresh方法

程序员欣宸

Java spring Spring Framework 6月月更

Fabric.js 激活输入框

德育处主任

fabric canvas Fabric.js 6月月更

Camtasia 2022发布更新功能介绍

茶色酒

Camtasia 2022

C#入门系列(九) -- 方法使用

陈言必行

C# 6月月更

InfoQ 极客传媒 15 周年庆征文| 迁移 Eureka 到 Nacos 之双注册双订阅模式

4ye

架构 nacos Eureka springcloudAlibaba InfoQ极客传媒15周年庆

文档书写规范

甜甜的白桃

文档 6月月更

TiDB Cloud 上线 Google Cloud Marketplace,以全新一栈式实时 HTAP 数据库赋能全球开发者

PingCAP

TiDB

盘点现有开源软件许可合规工具

开源社

10 个派上用场的 Flutter 小部件

坚果

6月月更

TypeScript 3.6正式发布!新增4项突破性特点_语言 & 开发_Daniel Rosenwasser_InfoQ精选文章