宣布 TypeScript 3.6 候选版本
今天我们很高兴地宣布推出TypeScript 3.6的候选版本。 此发布候选版本旨在与完整版本相当接近,并将在我们正式发布之前的几周内稳定下来。
要开始使用 RC 通过以下命令使用npm:
npm install -g typescript@rc
让我们来探索3.6中的内容!
Stricter Generators (对生成器更严格的检查机制)
TypeScript 3.6引入了对迭代器和生成器函数的更严格检查。 在早期版本中,生成器的用户无法区分是 yield
返回还是 return
的返回,这是两种不同的返回。
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'.
// 它应该知道它是'字符串',因为'done'是'true'!
curr.value
}
除此之外,generators 假设 yield
的 type
总是 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
类型现在允许用户指定生成的yielded
,returned
类型,以及next
可以接受的类型。
interface Iterator {
// Takes either 0 or 1 arguments - doesn't accept 'undefined'
next(...args: [] | [TNext]): IteratorResult;
return?(value?: TReturn): IteratorResult;
throw?(e?: any): IteratorResult;
}
为了区分 returned values和yielded values,TypeScript 3.6将IteratorResult类型转换为区分联合类型(To allow differentiation between returned values and yielded values, TypeScript 3.6 converts the IteratorResult type to a discriminated union type:)
type IteratorResult = IteratorYieldResult | IteratorReturnResult;
interface IteratorYieldResult {
done?: false;
value: TYield;
}
interface IteratorReturnResult {
done: true;
value: TReturn;
}
简而言之,这意味着在直接处理迭代器时,您将能够适当地缩小迭代器的值。
为了正确表示可以从调用next()
传递给生成器的类型,TypeScript 3.6还可以在生成器函数体内推断出yield
的某些用法。
function* foo() {
let x: string = yield;
console.log(x.toUpperCase());
}
let x = foo();
x.next(); // first call to 'next' is always ignored
x.next(42); // error! 'number' is not assignable to 'string'
如果您更喜欢显式,那么您还可以使用显式返回类型强制执行可以从yield表达式返回,生成和计算的值类型。 下面,next()只能用布尔值调用,并且根据done的值,value可以是字符串或数字。(If you’d prefer to be explicit, you can also enforce the type of values that can be returned, yielded, and evaluated from yield expressions using an explicit return type. Below, next() can only be called with booleans, and depending on the value of done, value is either a string or a number.)
/**
* - yields numbers
* - returns strings
* - can be passed in booleans
*/
function* counter(): Generator {
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!
For more details on the change, see the pull request here.
More Accurate Array Spread(更准确的数组展开)
在ES2015之前的目标中,对于诸如for/of
循环和数组展开的结构,最忠实的发射可能有点重。 因此,TypeScript默认使用更简单的emit,它只支持数组类型,并支持使用--downlevelIteration
标志迭代其他类型。 在此标志下,发出的代码更准确,但更大。
默认情况下--downlevelIteration
默认关闭效果很好,因为大多数以ES5为目标的用户只计划使用带数组的迭代结构。 但是,我们的只有支持数组的发射在某些边缘情况下仍然存在一些可观察到的差异。
例如,以下示例
[...Array(5)]
等同于:
[undefined, undefined, undefined, undefined, undefined]
但是,TypeScript会将原始代码转换为此代码:
Array(5).slice();
这略有不同。 Array(5)
生成一个长度为5的数组,但没有定义的属性槽!
1 in [undefined, undefined, undefined] // true
1 in Array(3) // false
当TypeScript调用slice()
时,它还会创建一个索引尚未设置的数组。
这可能看起来有点深奥,但事实证明许多用户遇到了这种不良行为。 TypeScript 3.6不是使用slice()和内置函数,而是引入了一个新的__spreadArrays帮助程序,以准确地模拟ECMAScript 2015中在--downlevelIteration之外的旧目标中发生的事情。 __spreadArrays也可以在tslib中使用(如果你正在寻找更小的包大小,那么值得一试)。
For more information, see the relevant pull request.
Improved UX Around Promises
Promise
是当今使用异步数据的最常用方法之一。 不幸的是,使用面向Promise
的API通常会让用户感到困惑。 TypeScript 3.6引入了一些改进,以防止错误处理Promise
。
例如,在将它传递给另一个函数之前忘记.then()
或await
Promise
的内容通常是很常见的。 TypeScript的错误消息现在是专用的,并告知用户他们可能应该考虑使用await
关键字。
interface User {
name: string;
age: number;
location: string;
}
declare function getUserData(): Promise;
declare function displayUser(user: User): void;
async function f() {
displayUser(getUserData());
// ~~~~~~~~~~~~~
// Argument of type 'Promise' is not assignable to parameter of type 'User'.
// ...
// Did you forget to use 'await'?
}
在await
或.then()
- Promise
之前尝试访问方法也很常见。 这是另一个例子,在许多其他方面,我们能够做得更好。
async function getCuteAnimals() {
fetch("https://reddit.com/r/aww.json")
.json()
// ~~~~
// Property 'json' does not exist on type 'Promise'.
//
// Did you forget to use 'await'?
}
目的是即使用户不知道使用await
,至少,这些提示消息提供了更多关于从何处开始的上下文。
与可发现性相同,让您的生活更轻松 - 除了Promises
上更好的错误消息之外,我们现在还在某些情况下提供快速修复。
For more details, see the originating issue, as well as the pull requests that link back to it.
import.meta
Support in SystemJS
当您的target: module
设置为system
时,TypeScript 3.6支持将import.meta
转换为context.meta
。
// This module:
console.log(import.meta.url)
// gets turned into the following:
System.register([], function (exports, context) {
return {
setters: [],
execute: function () {
console.log(context.meta.url);
}
};
});
get
and set
Accessors Are Allowed in Ambient Contexts(在上下文中允许编写getter setter)
在以前的TypeScript版本中,该语言不允许在环境上下文中使用get和set访问器(例如,在declare-d类中,或者在.d.ts文件中)。 理由是,就这些属性的写作和阅读而言,访问者与属性没有区别; 但是,因为ECMAScript的类字段提议可能与现有的TypeScript版本有不同的行为,我们意识到我们需要一种方法来传达这种不同的行为,以便在子类中提供适当的错误。
因此,用户可以在TypeScript 3.6中的环境上下文中编写getter和setter。
declare class Foo {
// Allowed in 3.6+.
get x(): number;
set x(val: number): void;
}
在TypeScript 3.7中,编译器本身将利用此功能,以便生成的.d.ts文件也将发出get / set访问器。
Ambient Classes and Functions Can Merge(环境类和函数可以合并)
在以前版本的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声明不能与名称空间合并)。
在TypeScript 3.7中,编译器将利用此功能,以便从.js文件生成的.d.ts文件可以适当地捕获类类函数的可调用性和可构造性。
For more details, see the original PR on GitHub.
APIs to Support --build and --incremental(API支持--build
--incremental
)
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的实例。
For more details on these APIs, you can see the original pull request.
Semicolon-Aware Code Edits(分号感知代码编辑)
Visual Studio和Visual Studio Code等编辑器可以自动应用快速修复,重构和其他转换,例如自动从其他模块导入值。 这些转换由TypeScript提供支持,旧版本的TypeScript无条件地在每个语句的末尾添加分号; 不幸的是,这不同于许多用户的风格指南,许多用户对编辑器插入分号感到不满。
TypeScript现在足够智能,可以在应用这些编辑时检测您的文件是否使用分号。 如果您的文件通常缺少分号,则TypeScript不会添加分号。
For more details, see the corresponding pull request.
Smarter Auto-Import Syntax(更智能的自动导入语法)
JavaScript有许多不同的模块语法或约定:ECMAScript标准中的一个,一个Node已经支持(CommonJS),AMD,System.js等等! 在大多数情况下,TypeScript默认使用ECMAScript模块语法自动导入,这在某些具有不同编译器设置的TypeScript项目中通常是不合适的,或者在具有纯JavaScript和需要调用的Node项目中。
在决定如何自动导入其他模块之前,TypeScript 3.6现在更加智能地查看现有导入。You can see more details in the original pull request here.
Breaking Changes(突破性的变化)
字符串方式命名constructor
会被解析成构造函数constructor
,根据ECMAScript规范,使用名为 constructor
的方法的类声明现在是构造函数,无论它们是使用标识符名称还是字符串名称声明。
class C {
"constructor"() {
console.log("I am the constructor now.");
}
}
一个值得注意的例外,以及此中断的解决方法是使用名称计算为“constructor”的计算属性。
class D {
["constructor"]() {
console.log("I'm not a constructor - just a plain method!");
}
}
DOM Updates (DOM更新)
许多声明已在lib.dom.d.ts
中删除或更改。 这包括(但不限于)以下内容:
- 全局
window
不再定义为类型Window
- 而是将其定义为类型Window&typeof globalThis
。 而是使用typeof window
。 -
GlobalFetch
已经不见了。 而是使用WindowOrWorkerGlobalScope
-
Navigator
上的某些非标准属性消失了。 -
experimental-webgl
上下文消失了。 相反,使用webgl
或webgl2
。
If you believe a change has been made in error, please file an issue!
JSDoc Comments Don’t Merge(JSDoc注释不合并)
在JavaScript文件中,TypeScript只会在JSDoc注释之前立即查询以确定声明的类型。
/**
* @param {string} arg
*/
/**
* oh, hi, were you trying to type something?
*/
function whoWritesFunctionsLikeThis(arg) {
// 'arg' has type 'any'
}
Keywords Cannot Contain Escape Sequences(关键字不能包含转义序列)
以前关键字不允许包含转义序列。 TypeScript 3.6不允许它们。
while (true) {
\u0063ontinue;
// ~~~~~~~~~~~~~
// error! Keywords cannot contain escape characters.
}
What’s Next?
TypeScript 3.6将于本月底发布。 我们希望您给这个版本一个机会,让我们知道它是如何工作的。 如果您有任何建议或遇到任何问题,请不要害怕通过问题跟踪器并打开问题!
Happy Hacking!(Happy黑客)
– Daniel Rosenwasser and the TypeScript Team
参考:
https://devblogs.microsoft.com/typescript/announcing-typescript-3-6-rc/