[译]TypeScript 2.1 候选版:更好的类型推断、异步函数和其它更多特性

今天我们非常高兴地宣布发布TypeScript 2.1 候选版!如果您不熟悉它,TypeScriptTypeScript中文网)是一种向JavaScript添加可选静态类型的语言,并将来自ES6和更高版本的新特性带到您使用的任何JavaScript运行时。

像往常一样,你可以通过NuGet得到RC,或仅通过运行

npm install -g typescript@rc

然后,您可以轻松地在Visual Studio Code我们的Sublime Text插件中使用RC版本。

在VS2015上安装Update 3后,就可以下载Visual Studio 2015的TypeScript 2.1安装包

虽然TypeScript 2.1有很多很棒的功能,但我们想要强调的是,TypeScript 2.1的类型推断功能将会更强大,以及更容易在所有运行时编写异步代码。

更加智能的类型推断

TypeScript 2.1现在可以更容易地模拟逐渐初始化变量的场景。由于很多代码在JavaScript中是这样写的,这使得将现有的代码库迁移到TypeScript变得更加容易。

为了更好地理解,让我们先谈谈any类型。

大部分的时间,如果TypeScript不能找出一个变量的类型,它会选择尽可能灵活不困扰你的any类型。我们经常称之为隐式any类型(而不是一个明确的你会写出来的类型)。

let x;      // 隐式 'any'
let y = []; // 隐式 'any[]'

let z: any; // 显式 'any'.

从这一点上,你可以使用这些值做任何你想要做的事情。对许多人来说,这种行为太松散了,这就是为什么--noImplicitAny编译参数会在无法推断类型时发出警告。

在TypeScript 2.0中,我们构建了使用控制流分析来跟踪整个程序中类型流的基础。因为该分析检查每个变量的赋值,我们在TypeScript 2.1中利用了同样的基础,更深入地检查了每一个变量的类型,它看起来像是一个更好的类型。而不是只是选择any,TypeScript将根据你最终的赋值来推断类型。

让我们来看下面的例子。

let x;

// 我们仍然可以给'x'赋任何我们需要的值。
x = () => 42;

// 在刚才赋值后,TypeScript 2.1 知道'x'的类型是'() => number',
// 因此它可以被调用。
x();

// 但现在我们会得到一个错误,我们不能添加一个数字到函数!
console.log(x + 42);
//          ~~~~~~
// 错误!运算符 '+' 不能应用于类型`() => number`和'number'。

// TypeScript仍然允许你给'x'赋值你需要的任何值。
x = "Hello world!";

// 但现在它仍然知道'x'是'string'类型的!
x.toLowerCase();

当涉及到赋值,TypeScript仍然会相信你,并允许你给x赋值任何你需要的值。然而,对于任何其他的用途,the type checker will know better by climbing up and looking at whatever you’ve actually done with x.

现在对空数组也进行同样的跟踪。这意味着更好的完成:

let puppies = [];

puppies.push(new Puppy());

for (let pup of puppies) {
    pup.bark();
    //  ^^^^ Get completion on 'bark'
}

这也意味着TypeScript可以捕获更多的明显的错误:

puppies[1] = new Kitty();

for (let pup of puppies) {
    pup.bark();
    //  ~~~~ 错误:'bark'不存在'Kitty'类型中
}

所有这一切的最终结果是,将来你会看到较少隐式any错误,并得到更好的工具支持。

低版本异步函数

TypeScript 2.1将支持低版本异步函数(即async/await),并且现在你就可以在候选版本中使用它!async/await是ECMAScript 2017版本中的新特性,它允许用户在promise中编写代码,而不需要使用回调。async函数写出来的代码风格看起来像同步代码那样,但使用await关键字异步运行。

该特性在TypeScript 2.1之前就已经支持了,但是只能编译为ES6或者ES2015。TypeScript 2.1使其该特性可以在ES3和ES5运行时上使用,这意味着无论您使用什么环境,都可以使用它。

例如,让我们使用下面名为delay的函数,它返回一个promise并在完成之前等待一定的时间:

function delay(milliseconds: number) {
    return new Promise<void>(resolve => {
      setTimeout(resolve, milliseconds);
    });
}

让我们尝试一个简单的探索任务。我们要写一个程序,打印"Hello",三个点,接着是"World!"

function welcome() {
    console.log("Hello");

    for (let i = 0; i < 3; i++) {
        console.log(".");
    }

    console.log("World!");
}

原来是听起来很简单。

现在让我们假设我们要使用我们的delay函数在每个点之前暂停。

没有async / await,我们必须写如下:

function dramaticWelcome() {
    console.log("Hello");

    (function loop(i){
        if (i < 3) {
            delay(500).then(() => {
                console.log(".");
                loop(i + 1);
            });
        }
        else {
            console.log("World!");
        }
    })(0);
}

这看起来不那么简单了!如果我们尝试使用async函数来使这个代码更具可读性呢?

首先,我们需要确保我们的运行时具有全局可用的符合ECMAScript的Promise。这可能需要获取Promise的polyfill,或依赖于你指定版本运行时中其中的一个。我们还需要确保TypeScript知道Promise存在,通过将我们的lib编译参数设置为像“dom”,“es2015”或“dom”,“es2015.promise”,“es5”:

{
    "compilerOptions": {
        "lib": ["dom", "es2015.promise", "es5"]
    }
}

现在我们可以使用asyncawait来重写代码:

async function dramaticWelcome() {
    console.log("Hello");

    for (let i = 0; i < 3; i++) {
        await delay(500);
        console.log(".");
    }

    console.log("World!");
}

请注意,与我们的同步版本的代码相比,这是多么相似!尽管它看起来像同步的,但这个函数实际上是异步的,并且不会阻止其他代码在每次暂停之间运行。事实上,dramaticWelcome的两个版本从基本上归结为相同的代码,但是使用async&await,TypeScript为我们提供了很大的帮助。

下一步

TypeScript 2.1 RC有很多其他特性,我们将有更多的适用于2.1。你可以看看我们的路线图,看看在存储区还有哪些特性。我们希望你尝试一下并且享受它!

发表评论