重磅!TypeScript 3.7 RC发布,备受瞩目的Optional Chaining来了
Posted 前端之巅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重磅!TypeScript 3.7 RC发布,备受瞩目的Optional Chaining来了相关的知识,希望对你有一定的参考价值。
-
可选链 -
空值合并 -
断言函数 -
更好地支持返回 never 的函数 -
(更多)递归类型别名 -
--declaration 和 --allowJs -
使用项目引用进行免构建编辑 -
未调用函数的检查 -
TypeScript 文件中的 // @ts-nocheck -
分号格式化选项 -
重大更改 -
DOM 更改 -
函数真实性检查 -
本地和导入的类型声明现在会冲突 API 更改
npm install typescript@rc
-
下载 Visual Studio 2019/2017。 查阅 Visual Studio Code 和 Sublime Text 的指引。
Visual Studio 2019/2017:https://marketplace.visualstudio.com/items?
Visual Studio Code:https://code.visualstudio.com/Docs/languages/typescript#_using-newer-typescript-versions
Sublime Text:https://github.com/Microsoft/TypeScript-Sublime-Plugin/#note-using-different-versions-of-typescript
下面,我们来逐一介绍 TypeScript 3.7 的新功能。首先是最受瞩目的功能:可选链(Optional Chaining)。
TypeScript 3.7 实现了呼声最高的 ECMAScript 功能之一:可选链(Optional Chaining)!我们的团队一直在深度参与 TC39 的标准制定,努力将这一功能推向第三阶段,从而将其带给所有的 TypeScript 用户。
let x = foo?.bar.baz();
let x = (foo === null || foo === undefined) ?
undefined :
foo.bar.baz();
请注意,如果 bar 为 null 或 undefined,我们的代码在访问 baz 时仍会出错。同样,如果 baz 为 null 或 undefined,我们在调用函数时也会出现错误。?. 只会检查其左侧的值是否为 null 或 undefined,而不检查任何后续属性。
// 之前
if (foo && foo.bar && foo.bar.baz) {
// ...
}
// 之后
if (foo?.bar?.baz) {
// ...
}
请记住?. 与 && 操作的行为有所不同,其中 && 操作特别针对的是“虚假”值(例如空字符串、0、NaN 和 false)。
/**
* 当我们有一个数组时,返回它的第一个元素
* 否则返回 undefined。
*/
function tryGetFirstElement<T>(arr?: T[]) {
return arr?.[0];
// 等效:
// return (arr === null || arr === undefined) ?
// undefined :
// arr[0];
}
async function makeRequest(url: string, log?: (msg: string) => void) {
log?.(`Request started at ${new Date().toISOString()}`);
// 等效:
// if (log !== null && log !== undefined) {
// log(`Request started at ${new Date().toISOString()}`);
// }
const result = (await fetch(url)).json();
log?.(`Request finished at at ${new Date().toISOString()}`);
return result;
}
let result = foo?.bar / someComputation()
let temp = (foo === null || foo === undefined) ?
undefined :
foo.bar;
let result = temp / someComputation();
function barPercentage(foo?: { bar: number }) {
return foo?.bar / 100;
// ~~~~~~~~
// 错误:对象可能未定义。
}
有关更多详细信息可以查阅提案和原始的拉取请求。
提案:https://github.com/tc39/proposal-optional-chaining/](https://github.com/tc39/proposal-optional-chaining/
拉取请求:https://github.com/microsoft/TypeScript/pull/33294
另一个即将推出的 ECMAScript 功能是 空值合并运算符(nullish coalescing operator),它与可选链都是我们的团队一直努力推进的功能。
let x = foo ?? bar();
这是一种新的途径来表示值 foo“存在”时会被使用;但当它为 null 或 undefined 时,就在其位置计算 bar()。
let x = (foo !== null && foo !== undefined) ?
foo :
bar();
function initializeAudio() {
let volume = localStorage.volume || 0.5
// ...
}
当 localStorage.volume 设置为 0 时,页面会意外地将 volume 设置为 0.5。?? 能避免将 0、NaN 和""中的某些意外行为视为虚假值。
我们非常感谢社区成员 Wenlu Wang 和 Titian Cernicova Dragomir 实现此功能!有关更多详细信息,请查看其拉取请求和空值合并提案存储库。
拉取请求:https://github.com/microsoft/TypeScript/pull/32883
空值合并提案存储库:https://github.com/tc39/proposal-nullish-coalescing/
assert(someValue === 42);
在此示例中,如果 someValue 不等于 42,则 assert 将抛出 AssertionError。
function multiply(x, y) {
assert(typeof x === "number");
assert(typeof y === "number");
return x * y;
}
function yell(str) {
assert(typeof str === "string");
return str.toUppercase();
// 哇!我们拼错了 'toUpperCase'。
// 希望 TypeScript 还是能捕获它!
}
function yell(str) {
if (typeof str !== "string") {
throw new TypeError("str should have been a string.")
}
// 错误捕获!
return str.toUppercase();
}
TypeScript 的终极目标是以破坏最少的方式嵌入现有的 JavaScript 结构中,所以 TypeScript 3.7 引入了一个称为“断言签名”的新概念,可以对这些断言函数建模。
function assert(condition: any, msg?: string): asserts condition {
if (!condition) {
throw new AssertionError(msg)
}
}
function yell(str) {
assert(typeof str === "string");
return str.toUppercase();
// ~~~~~~~~~~~
// 错误:属性 'toUppercase' 在 'string' 类型上不存在。
// 你说的是 'toUpperCase' 吗?
}
function assert(condition: any, msg?: string): asserts condition {
if (!condition) {
throw new AssertionError(msg)
}
}
function assertIsString(val: any): asserts val is string {
if (typeof val !== "string") {
throw new AssertionError("Not a string!");
}
}
function yell(str: any) {
assertIsString(str);
// 现在 TypeScript 知道 'str' 是一个 'string'.
return str.toUppercase();
// ~~~~~~~~~~~
// 错误:属性 'toUppercase' 在 'string' 类型上不存在。
// 你说的是 'toUpperCase' 吗?
}
function isString(val: any): val is string {
return typeof val === "string";
}
function yell(str: any) {
if (isString(str)) {
return str.toUppercase();
}
throw "Oops!";
}
function assertIsDefined<T>(val: T): asserts val is NonNullable<T> {
if (val === undefined || val === null) {
throw new AssertionError(
`Expected 'val' to be defined, but received ${val}`
);
}
}
有关断言签名的更多信息,请查看原始的拉取请求。
拉取请求:https://github.com/microsoft/TypeScript/pull/32695
作为断言签名项目的一部分,TypeScript 需要对调用的函数和调用的位置编码更多信息。于是我们就可以扩展对另一类函数的支持:返回 never 的函数。
任何返回 never 的函数的意图都是说它不会返回。它表明抛出了异常,出现了暂停错误条件或程序已退出。例如,@types/node 中的 process.exit(...) 被指定为返回 never。
function dispatch(x: string | number): SomeType {
if (typeof x === "string") {
return doThingWithString(x);
}
else if (typeof x === "number") {
return doThingWithNumber(x);
}
return process.exit(1);
}
function dispatch(x: string | number): SomeType {
if (typeof x === "string") {
return doThingWithString(x);
}
else if (typeof x === "number") {
return doThingWithNumber(x);
}
process.exit(1);
}
你可以在断言函数的拉取请求中查阅更多信息。
type Foo = Foo;
这是一个合理的限制,因为任何对 Foo 的使用都需要用 Foo 代替,而 Foo 则需要用 Foo 代替,而 Foo 则需要用 Foo 代替……好吧,希望你懂我的意思!最后没有哪一种类型可以代替 Foo。
type ValueOrArray<T> = T | Array<ValueOrArray<T>>;
// ~~~~~~~~~~~~
// 错误:类型别名 'ValueOrArray' 循环引用它自己。
type ValueOrArray<T> = T | ArrayOfValueOrArray<T>;
interface ArrayOfValueOrArray<T> extends Array<ValueOrArray<T>> {}
由于接口(和其他对象类型)引入了一定程度的间接性,并且不需要急切地构建其完整结构,因此 TypeScript 在使用该结构时没有问题。
但对于用户而言,引入界面的解决方法并不直观。原则上来说,ValueOrArray 的原始版本,也就是直接使用 Array 确实没有任何问题。如果编译器有点“懒惰”,并且仅在必要时才计算 Array 的类型参数,则 TypeScript 就可以正确表达这些参数。
这正是 TypeScript 3.7 引入的内容。在类型别名的“顶层”,TypeScript 将推迟解析类型参数以允许使用这些模式。
type Json =
| string
| number
| boolean
| null
| JsonObject
| JsonArray;
interface JsonObject {
[property: string]: Json;
}
interface JsonArray extends Array<Json> {}
type Json =
| string
| number
| boolean
| null
| { [property: string]: Json }
| Json[];
type VirtualNode =
| string
| [string, { [key: string]: any }, ...VirtualNode[]];
const myNode: VirtualNode =
["div", { id: "parent" },
["div", { id: "first-child" }, "I'm the first child"],
["div", { id: "second-child" }, "I'm the second child"]
];
有关更多信息可以查阅原始的拉取请求。
拉取请求:https://github.com/microsoft/TypeScript/pull/33050
TypeScript 中的 --declaration 标志允许我们从源 TypeScript 文件(如.ts 和.tsx 文件)生成.d.ts 文件(声明文件)。这些.d.ts 文件很重要,因为它们允许 TypeScript 对其他项目进行类型检查,而无需重新检查 / 构建原始源代码。出于相同的原因,使用项目引用时 需要 此设置。
不幸的是,--declaration 不适用于 --allowJs 之类的设置,无法混合使用 TypeScript 和 JavaScript 输入文件。这是一个令人沮丧的限制,因为它意味着用户即使在迁移代码库时也无法使用 --declaration,即使使用 JSDoc 注释也是如此。TypeScript 3.7 对此做了更改,并允许将这两种功能混合使用!
/**
* @callback Job
* @returns {void}
*/
/** Queues work */
export class Worker {
constructor(maxDepth = 10) {
this.started = false;
this.depthLimit = maxDepth;
/**
* 注意:队列中的作业可能会将更多项目添加到队列中
* @type {Job[]}
*/
this.queue = [];
}
/**
* 在队列中添加一个工作项
* @param {Job} work
*/
push(work) {
if (this.queue.length + 1 > this.depthLimit) throw new Error("Queue full!");
this.queue.push(work);
}
/**
* 如果队列尚未开始就启动它
*/
start() {
if (this.started) return false;
this.started = true;
while (this.queue.length) {
/** @type {Job} */(this.queue.shift())();
}
return true;
}
}
/**
* @callback Job
* @returns {void}
*/
/** Queues work */
export class Worker {
constructor(maxDepth?: number);
started: boolean;
depthLimit: number;
/**
* 注意:队列中的作业可能会将更多项目添加到队列中
* @type {Job[]}
*/
queue: Job[];
/**
* 在队列中添加一个工作项
* @param {Job} work
*/
push(work: Job): void;
/**
* 如果队列尚未开始就启动它
*/
start(): boolean;
}
export type Job = () => void;
有关更多详细信息请查阅原始拉取请求。
拉取请求:https://github.com/microsoft/TypeScript/pull/32372
TypeScript 的项目引用为我们提供了一种简便的方法来分解代码库,从而提升编译速度。不幸的是,编辑一个尚未建立依赖关系(或输出已过时)的项目时会出现很多问题。
在 TypeScript 3.7 中,当打开具有依赖项的项目时,TypeScript 将自动使用源.ts / .tsx 文件代替。这意味着使用项目引用的项目现在将获得增强的编辑体验,其中语义操作都是最新且“有效”的。你可以使用编译器选项 disableSourceOfProjectReferenceRedirect 禁用此行为,处理非常大的项目(此更改可能影响编辑性能)时用这个选项可能会很合适。
有关此更改的更多信息请查阅其拉取请求。
拉取请求:https://github.com/microsoft/TypeScript/pull/32028
interface User {
isAdministrator(): boolean;
notify(): void;
doNotDisturb?(): boolean;
}
// 稍后……
// 代码已破坏, 勿用!
function doAdminThing(user: User) {
// oops!
if (user.isAdministrator) {
sudo();
editTheConfiguration();
}
else {
throw new AccessDeniedError("User is not an admin");
}
}
比如在上面的代码中我们忘记调用 isAdmini-strator,该代码错误地允许非管理员用户编辑配置!
function doAdminThing(user: User) {
if (user.isAdministrator) {
// ~~~~~~~~~~~~~~~~~~~~
// 错误!这个条件将始终返回 true,因为这个函数定义是一直存在的
// 你的意思是调用它吗?
interface User {
isAdministrator(): boolean;
notify(): void;
doNotDisturb?(): boolean;
}
function issueNotification(user: User) {
if (user.doNotDisturb) {
// OK, 属性是可选的
}
if (user.notify) {
// OK, 调用了这一函数
user.notify();
}
}
如果你打算在不调用函数的情况下测试它,则可以将其定义更正为包含 undefined/null,或使用!! 编写类似 if(!!user.isAdministrator) 的内容以指示强制是故意的。
我们非常感谢 GitHub 用户 @jwbay,他创建了概念验证,并一直改进到现在这个版本。
TypeScript 3.7 允许我们在 TypeScript 文件的顶部添加 // @ts-nocheck 注释以禁用语义检查。过去只有在存在 checkJs 时,JavaScript 源文件中的这一注释才会被认可。但我们已扩展了对 TypeScript 文件的支持,以简化所有用户的迁移过程。
TypeScript 的内置格式化程序现在支持在结尾分号可选的位置,根据 JavaScript 的自动分号插入(ASI)规则插入和删除分号。该设置现在在 Visual Studio Code Insiders 中可用,也在 Visual Studio 16.4 Preview 2 中的“Tools Options”菜单中可用。
选择“insert”或“remove”的值还会影响自动导入的格式、提取的类型以及 TypeScript 服务提供的其他生成代码。将设置保留为默认值“ignore”会使生成的代码与当前文件中检测到的分号首选项相匹配。
lib.dom.d.ts 中的类型已更新。这些更改大都是与可空性相关的正确性更改,但其影响最终取决于你的代码库。
https://github.com/microsoft/TypeScript/pull/33627
-
检查值来自可选属性。 -
strictNullChecks 已禁用。 该函数稍后在 if 的正文中调用。
// ./someOtherModule.ts
interface SomeType {
y: string;
}
// ./myModule.ts
import { SomeType } from "./someOtherModule";
export interface SomeType {
x: number;
}
function fn(arg: SomeType) {
console.log(arg.x); // Error! 'x' doesn't exist on 'SomeType'
}
在这里,SomeType 似乎同时源于 import 声明和本地 interface 声明。可能想不到的是,在模块内部 SomeType 仅引用 import 的定义,而本地声明 SomeType 仅在从另一个文件导入时才可用。这非常令人困惑,我们对这类罕见情况的调查表明,开发人员通常会认为出现了一些其他问题。
TypeScript 3.7 现在可以正确地将其识别为重复的标识符错误。正确的修复方案取决于作者的初衷,具体情况具体分析。通常来说,命名冲突是无意的,最好的解决方法是重命名导入的类型。如果初衷是要扩展导入的类型,则应编写适当的模块扩展。
为了支持前文所述的递归类型别名模式,我们已从 TypeReference 接口中删除了 typeArguments 属性。用户应该在 TypeChecker 实例上使用 getTypeArguments 函数。
TypeScript 3.7 的最终版本将在几周内发布,理想情况下只需极少的更改即可完成!我们希望大家尝试这个 RC 版本并向我们提供反馈,从而打造一个出色的 3.7 版本。如果你有任何建议或遇到任何问题,请随时在我们的问题跟踪器上报告问题!
https://github.com/microsoft/TypeScript/issues/new/choose
编程快乐!
——Daniel Rosenwasser 和 TypeScript 团队
原文链接:https://devblogs.microsoft.com/typescript/announcing-typescript-3-7-rc/
近年来,随着 jQuery 的落幕,三大框架的割据以及小程序的爆发,大前端的发展也经历了从静态页面到 JavaScript 跨时代的诞生,再从 PC 端到移动端的转向,以及由依赖后端到前后端分离的架构演变。
腾讯在线教育前端团队,近年来在大前端技术架构演进方面也有了不少突破,如 Hybird 方案、离线包方案、PWA 结合 SSR 方案、以及 RN 动态化方案的落地和执行等。
这次 GMTC 全球大前端技术大会(深圳站)2019,我们专门请到了 腾讯的前端高级工程师曹海歌,希望可以从腾讯在线教育前端团队的实操案例中,深入了解腾讯为提升研发效率,进行的前端工程化体系建设过程。扫描下方二维码或点击阅读原文,查看详情。
以上是关于重磅!TypeScript 3.7 RC发布,备受瞩目的Optional Chaining来了的主要内容,如果未能解决你的问题,请参考以下文章
如何添加 TypeScript 3.7 可选链接支持来创建反应应用项目
重磅:第十二届中国西部国际资本论坛盛大举办,分布式存储行业备受瞩目!