专栏首页腾讯IVWEB团队的专栏[译] TypeScript4有些啥?

[译] TypeScript4有些啥?

原文: What's coming in TypeScript 4 作者: Tim Perry

TypeScript4来得很快, 这周(6.25)就有一个Beta Release的计划, 而最终的正式release将会在八月中旬到来.

需要注意的是, TypeScript的Release并没有遵循Semantic版本规则进行, 所以4.0版本并不算是一个大型的更新.

TypeScript never claimed to follow semantic versioning, in the sense that breaking changes imply major versions. TypeScript, however, promises no breaking changes after a stable release. so no breaking changes between 2.1.5 and 2.1.6, 2.1.*. TypeScript从来没有说过遵循Semantic版本规则, 即断代更新会体现在主版本号变更上相反, TypeScript承诺在每一个Stable Release之间不会有断代更新, 因此在2.1.5, 2.1.6, 2.1.*...上面不存在断代更新

版本号跳到了4.0并不意味着这里有什么大型的断代, 这不是啥改变一切的Release, 但他还是确确实实带来了一些关于类型的棒棒的改动. 对于像HTTP Toolkit(完全是TypeScript开发的)的项目来说, 这次改动能够带来更快的开发速度以及更少的bug.

让我们来深入一下细节的部分.

可变元组类型 Variadic Tuple Type

所谓"可变类型", 对于TypeScript的类型系统来说, 是一个复杂但是颇具重要性的新Feature.

如果没有深刻理解类型理论的话, 解释这玩意儿会有点麻烦, 但是举个例子还是很简单的. 让我们试试用元组参数来书写一个concat函数:

function concat(
    nums: number[],
    strs: string[]
): (string | number)[] {
    return [...nums, ...strs];
}

let vals = concat([1, 2], ["hi"]);
let val = vals[1]; // 尽管我们知道这是一个number, 但TS仍然会提示这是一个number|string

// 但TS确实支持对这些值的精确的类型定义
let typedVals = concat([1, 2], ["hi"]) as [number, number, string];
let typedVal = typedVals[1] // => 提示number, 没问题

在目前, 这段代码是合法的TS代码, 但并不是最优的.

这段代码中, concat能够正确地起作用, 但是我们会丢失一些类型, 而且如果想要在其他地方获得精确的类型的话, 就不得不在之后手动地进行修正. 目前还没有可能能够完全避免这些问题.

在有了可变元组之后, 我们可以这样写

function concat<N extends number[], S extends string[]>(
    nums: [...N],
    strs: [...S]
): [...N, ...S] {
    return [...nums, ...strs];
}

let vals = concat([1, 2], ["hi"]);
let val = vals[1]; // => 提示类型 number
const val2 = vals[1]; // => 提示值 2, 而不只是类型number

// 在深入一点, 我们可以精确地concat任何类型
function concat<T extends unknown[], U extends unknown[]>(
    t: [...T],
    u: [...U]
): [...T, ...U] {
    return [...t, ...u];
}

本质上来说, 元组类型现在能够包含...T作为一个多个元组内类型的泛型占位. 可以通过此来形容一些未知类型([...T]), 或者部分类型已知的元组([string, ...T, boolean, ...U]).

TypeScript能够在之后使用的过程中提示这些类型, 因此只需要在大体地对元组形状进行描述并在之后使用, 而不需要依赖具体的细节.

这是一种相对简洁的方式, 并且比简单地连接数组要来的更为广泛. 通过组合一些已经存在的可变函数, 例如f<T extends unknown[]>(...args: [...T]), 就能够把函数的参数当做数组来看待, 进而能够比现在更具弹性地去描述函数的参数格式.

举个例子, 目前对函数中剩余/可变参数的描述必须始终放在函数参数描述的末尾, f(a: number, ...b:string[], c: boolean)便是一个无效的例子

在这一次的升级之后, 通过在函数参数定义中使用可变元组类型, f<T extends string[]>(...args: [number, ...T, boolean])便能使上述的例子成为可能

看起来有点抽象, 实际上可以这么理解, 我们能通过这一特性来做到:

  • 解构数组类型: type head = <H extends unkone, T extends unknonw[]>(list: [H, ...T]) => H
  • 对任意长度的数组执行类似映射类型才允许的操作, 而不仅仅是对象
  • 对可变参数的函数进行完整的类型提示
  • 对复杂的, 部分参数类型已知的可变参数进行正确的提示
  • 对Promisify进行完整的类型定义
  • 对诸如curry, apply, compose等高阶函数进行完整地参数类型描述
  • 干掉所有的为了完整描述类型而产生的冗余代码.

就算现在没在写什么复杂的高阶函数, 改进类型也仍然能让我们在之后的能够更细节地去描述类型, 正确提示一些不明确的数组类型定义, 改进其他地方的类型提示.

带标注的元组 Labelled Tuples

这是一个跟上一个有关系, 但是要简单得多的特性: TypeScript将允许给元组中的元素加上标注了.

看看下面这个函数的类型的描述, 你能从中获得什么信息?

function getSize(): [number, number];

那这个呢

function getSize(): [min: number, max: number];

这些标注会在运行时消失(编译时会被注释掉), 并且也不会做额外的类型检查. 它们能像上面那样使得元组的声明变得更加直观清晰.

对于剩余/可选参数同样适用:

type MyTuple = [a: number, b?: number, ...c: number[]];

从构造函数的使用来推断属性类型

一个简明的类型提示的改进

class X {

    private a;

    constructor(param: boolean) {
        if (param) {
            this.a = 123;
        } else {
            this.a = false;
        }
    }

}

在上面这段代码中, 目前的TS版本中, a会被认为是any的类型(甚至还会在noImplicitAny开启时报错). 属性的类型只会在直接初始化的时候得到推断. 因此需要一个初始化函数, 或者直接对其进行定义.

在TypeScript4中, a的类型会被推断为number | boolean: 从构造函数自动推断.

如果这种机制还不能满足, 仍然能够通过精准定义的方式来对属性进行类型声明, 并且当这类声明存在时, 他们会被更优先地使用.

短路赋值操作符

对类型方面的改进不感兴趣? 没问题, TypeScript4.0同时实现了处于Stage3的JS特性: 逻辑运算赋值. 新的语法得到支持, 并会被编译到老的环境中也能运行的形式.

看起来就像这样

a ||= b
// 等于: a = a || b

a &&= b
// 等于: a = a && b

a ??= b
// 等于: a = a ?? b

目前, 最后一个选项可能是最有用的, 除非正儿八经地在进行布尔运算, 那这个合并运算对于默认值和错误回落值是个很完美的解决方案.

一些其他的部分

上面都是些主要的改动, 下面是一些其他的也很不错的部分:

  • unknown现在能够做为try..catch...中的类型标注 try {...} catch (e: unknown) {...}
  • 支持了React的新的JSX内部(译注: children之类的)
  • 编辑器对@deprecatedJSDoc注解的支持
  • 在3.9版本的性能提升后的性能提升
  • 新的编辑器可用的代码重构(比如自动的用可选链Optional Chaining进行重构), 改进了一些重构(更好的auto-import), 以及一些语法高亮

上述这些改动都不是大型的改动, 但也值得重视. 至少它们帮着TypeScript的程序员们续命了 -- 改善了类型安全以及开发体验.

当然需要注意的是, 这些并不是最终敲定的改动, 文章跳过一些讨论过但并没有被实现的特性, 从awaited T到占位符类型(这些特性可能下个月突然就冒出来了), 并且上面这些已经实现了的特性中也有可能因为一些不可避免的因素发生改变之类的. 所以要保持关注...

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • NOW 直播和微信小程序那些事

    微信小程序从发布开始,可谓赚足了眼球,一度引发了开发界“全民学JavaScript”的梗。为了跟上时代步伐,我们NOW直播团队也在发布后第一时间尝鲜,本文就来扒...

    腾讯IVWEB团队
  • 「NGW」前端新技术赛场:Serverless SSR 技术内幕

    Serverless 云函数: 云计算发展过程中出现的一种计算资源的抽象,它以云计算平台为基础,为开发者提供业务程序的运行环境,开发者无需关注底层资源分配、扩容...

    腾讯IVWEB团队
  • 如何写一个通用的README规范

    我们平常在进行项目开发时,一般都会把代码上传至代码托管平台上方便管理和维护。目前大家使用的托管平台最多的还是Github,国内外还有一些比较知名的代码托管平台,...

    腾讯IVWEB团队
  • 教你一招:用 50 行 Python 代码制作一个计算器

    简介 在这篇文章中,我将向大家演示怎样向一个通用计算器一样解析并计算一个四则运算表达式。当我们结束的时候,我们将得到一个可以处理诸如 1+2*-(-3+2)/5...

    CDA数据分析师
  • 自守数算法

    25 ^ 2 = 625 76 ^ 2 = 5776 9376 ^ 2 = 87909376

    morixinguan
  • python3 随机数 质数 素数 文

    最近工作有点忙 但还是每天坚持花了 三 四个小时学习了一下 python 不错 代码简洁 我很喜欢 还会继续。。。。以前学过点java 相对来说还是比java ...

    py3study
  • 15-TypeScript策略模式

    在前面的简单工厂模式中,通常将每个类、接口定义到不同的文件中。在面向对象开发思想中有一个重要的原则就是封装变化点,在实际操作过程中, 通常被调用方的代码不要去更...

    用户1910585
  • 自定义RecyclerView监听滑动到底部Bottom

    最近在做一个本地的万能播放器,需要监听RecyclerView滑动到底部,向用户提示已经滑动到最底部;看了网上其他童鞋的写法,比较繁琐。现在给出我的实现方法,非...

    GitOPEN
  • 遇上了这几个小程序,你肯定想为他生孩子

    你有没有过这样的经验?原本温柔可爱的女性朋友或者是同事,脾气在某一天突然变得无比暴躁,就像点着的雷一样一触即爆。

    知晓君
  • 小朋友学Java(8):重写与重载

    一、重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写! 重写的好处在于子类可以根...

    海天一树

扫码关注云+社区

领取腾讯云代金券