快捷搜索:

您的位置:澳门新葡4473网站 > 热门贴子 > TypeScript 3.6 发布,微软脚本编程语言

TypeScript 3.6 发布,微软脚本编程语言

发布时间:2020-03-30 05:44编辑:热门贴子浏览(128)

    TypeScript 3.6 已经发布了,更新内容如下:

    JavaScript子集

    大多数语言都会定义它们的子集,用以更安全地执行不信任的第三方代码。

    使用更严格的生成器(Stricter Generators)

    TypeScript 3.6 引入了对迭代器和生成器函数(generator functions)的更严格检查。在早期版本中,生成器的使用者无法区分一个值是来自 yield 操作或是从生成器返回。

    图片 1

    另外,生成器仅仅假设 yield 类型是任意的:

    图片 2

    以第一段代码为例,在 TypeScript 3.6 中,检查器会知道 curr.value 的正确类型应为 string,并在最后一个示例中纠正对 next() 的错误调用。这要归功于因在IteratorIteratorResult类型声明中进行的一些更改而引入部分新的类型参数,以及 TypeScript 用于代表称为Generator类型的生成器。

    该版本中 Iterator 类型允许用户说明 yield 类型、返回的类型和 next 可以接受的类型。

    图片 3

    在此基础上,新类型 Generator 是一个 Iterator,它同时存在 return 和 throw 方法,并且是可迭代的。

    图片 4

    为了区分返回的值和生成的值,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 用途。

    图片 5

    如果你希望显式,还可以使用显式返回类型从 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!
    

    精华(The Good Parts)

    Douglas Crockford曾写过一本很簿的书《JavaScript: The Good Parts》,专门介绍JavaScript中值得发扬光大的精华部分。这个语言子集的目标是规避语言中的怪癖、缺陷部分,最终编程更轻松、程序更健壮。

    • 使用函数定义表达式而不是函数定义语句来定义函数。
    • 循环体和条件分支都使用花括号括起来。
    • 任何语句只要不是以花括号结束都应该使用分号做结尾。
    • 推荐使用"==="和"!==",不推荐使用"=="和"!="(因为比较时会涉及到类型转换)。
    • 由于JavaScript并不包含块级作用域,var语句最好出现在函数体的顶部。
    • 禁止使用全局变量。

    Crockford写过一个在线代码质量检测工具JSLint,可通过这个工具对代码进行检查。

    更精确的数组扩展

    在默认情况下,TypeScript 使用一个更简单的发射,它只支持数组类型,并支持使用 --downlevelIteration 标志在其他类型上进行迭代。在此标志下,发出的代码更准确,但要大得多。但是,在某些边缘情况下,受支持的数组还是存在一些差异,如下:

    [...Array(5)]
    

    等效于以下数组:

    [undefined, undefined, undefined, undefined, undefined]
    

    但是,TypeScript 将原始代码转换为以下代码:

    Array(5).slice();
    

    这是不一样的,Array(5) 生成长度为 5 的数组,但没有属性:

    图片 6

    当 TypeScript 调用 Slice( ) 时,它还会创建一个数组,其中包含尚未设置的索引。

    3.6 版本引入了一个新的 __spreadArrays 助手,用于精确地模拟 ECMAScript 2015 中在 --downlevelIteration 之外中发生的情况。__SpreadArray 也可在 tslib 中使用。

    子集的安全性

    子集的设计目的是能在一个容器或"沙箱"中更安全地运行不可信的第三方JavaScript代码。所有能破坏这个沙箱并影响全局执行环境的语言特性和API在这个安全子集中都是禁止的。
    为了让JavaScript代码静态地通过安全检查,必须移除一些JavaScript特性:

    • 禁止使用this关键字,因为函数(在非严格模式中)可能通过this访问全局对象。
    • 禁止使用with语句,因为with语句增加了静态代码检查的难度。
    • 静态分析可以有效地防止带有点(.)运算符的属性存取表达式去读写特殊属性。但我们无法对方括号([])内的字符串表达式做静态分析。基于这个原因,安全子集禁止使用方括号,除非括号内是一个数字或字符串直接量。
    • eval()和Function()构造函数在任何子集里都是禁止使用的,因为它们可以执行任意代码,而且JavaScript无法对这些代码做静态分析。
    • 禁止使用全局变量,因此代码中不能有对Window对象的引用和对Document的引用。
    • 禁止使用某些属性和方法,以免在水箱中的代码拥有过多的权限。如:arguments对象的两个属性caller和callee、函数的call()和apply()方法、以及constructor和prototype两个属性。

    Improved UX Around Promises

    Promise 是当前处理异步数据的最常见方法之一,不幸的是,使用Promise面向对象的 API 通常会让使用者感到困惑。因此针对Promise被错误处理的情况,TypeScript 3.6 引入了一些改进。

    例如,在将其传递给另一个函数之前,Promise忘记.then()或者await内容通常是很常见的。TypeScript 的错误消息现在是专用的,并告知使用者他们可能应该考虑使用await关键字。

    图片 7

    另外还提供快速修复:

    图片 8

    JavaScript扩展

    更好的 Unicode 支持标识符

    当发射到 ES 2015或其他时,3.6 版本包含更好地支持标识符中的 Unicode 字符:

    图片 9

    常量和局部变量

    在JavaScript1.5及后续版本中可以使用const关键字来定义常量,常量是不可重复赋值的变量。

    const pi = 3.14;
    pi = 4;             // 对常量赋值会报"TypeError"
    

    在JavaScript1.7中,添加了关键字let,可以定义带有作用域的变量:
    下面列举各种场景使用let的作用域:

    • 全局作用域(定义在函数之外)
    let me = 'go';  // 全局作用域
    var i = 'able'; // 全局作用域
    
    • 函数作用域
    function ingWithinEstablishedParameters() {
        let terOfRecommendation = 'awesome worker!';    // 函数作用域
        var sityCheerleading = 'go!';                   // 函数作用域
    };
    
    • 块作用域
    function allyIlliterate() {
        // tuce在这里不可见
    
        for( let tuce = 0; tuce < 5; tuce++ ) {
            // tuce在这里可见
        };
    
        // tuce在这里不可见
    };
    
    function byE40() {
        // nish在这里可见
    
        for( var nish = 0; nish < 5; nish++ ) {
            // nish在这里可见
        };
    
        // nish在这里可见
    };
    

    更多可参考:“let” keyword vs “var” keyword

    例子如下:

    o = {x:1, y:2};
    for each(let v in o) 
      console.log(v);       // 输出1和2
    console.log(v);         // 错误:v is not defined
    

    在for循环中使用let关键字:

    // 输出 5, 5, 5, 5, 5
    for (var i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }
    
    // 输出 1, 2, 3, 4, 5
    for (let i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }
    

    更多可参考:Explanation of let and block scoping with for loops

    注:var声明的变量在它们所声明的函数内始终是存在的,但直到代码执行行到var语句时才初始化变量,在var语句执行之前它的值是undefined。通过let声明的变量也与之类似。

    在 SystemJS 中支持 import.meta

    3.6 版本支持在将 module 目标设置为 system 时,将 import.meta 转换为 context.meta。

    图片 10

    解构赋值(destructuring assignment)

    在解构赋值中,等号右侧是一个数组或对象(一个结构化的值),指定左侧一个或多个变量的语法和右侧的数组和对象直接量的语法保持格式一致。

    get and set Accessors Are Allowed in Ambient Contexts

    在早期版本的 TypeScript 中,并不允许在环境上下文中 set 和 get 访问器,其基本原理是,就写入和读取这些属性而言,访问器与属性并没有区别;然而,由于 ECMAScript 的类字段提案可能与现有版本的 TypeScript 中的行为不同,则需要一种方法来沟通这种不同的行为,以便在子类中提供适当的错误。

    因此 TypeScript 3.6 中,用户可以在环境上下文中编写 getter 和 setter。

    图片 11

    数组解构赋值

    • 简单的解构赋值
    let [x, y] = [1, 2];    // 等价于let x=1, y=2
    [x, y] = [x+1, y+1];    // 等价于x=x+1, y=y+1
    [x, y] = [y, x];        // 交换2个变量的值
    console.log([x, y]);    // => [3, 2]
    
    • 解构赋值左右的变量不一定要一一对应,左侧多余的变量赋值为undefined,而右侧多余的值则会忽略。左侧的变量列表可以包含连续的逗号用以跳过右侧对应的值。
    let [x, y] = [1];       // x = 1, y = undefined
    [x, y] = [1, 2, 3];     // x = 1, y = 2
    [, x, y] = [1, 2, 3, 4];// x = 2, y = 3
    

    注:JavaScript并未提供将右侧多余的值以数组的形式赋值给左侧变量的语法。比如,上面代码的第2行,并不能将[2,3]赋值给y。

    • 解构赋值的返回值是右侧的整个数据结构,而不是从中取出的某个值。
    let first, second, all;
    all = [first, second] = [1, 2, 3, 4];   // first=1, second=2, all=[1,2,3,4]
    
    • 解构赋值可使用数组嵌套的语法。
    let [one, [twoA, twoB]] = [1, [2, 2,5], 3]; // one=1, twoA=2, twoB=2.5
    

    环境类和函数可以合并

    在以前版本中,在任何情况下合并类和函数都是有错的。此版本中,环境类和函数(带有 declare 修饰符的类/函数,或 .d.ts 文件中的类/函数)可以合并。如下内容:

    图片 12

    而不需要使用:

    图片 13

    它的一个优点是可以轻松地表达可调用的构造函数模式,同时允许名称空间与这些声明合并(因为 var 声明不能与名称空间合并)。

    对象解构赋值

    • 在这种情况下,解构赋值的左侧看起来是一个对象直接量,对象中是一个名值对的列表,冒号右侧的值是变量名
    let transparent = {r:0.0, g:0.0, b:0.0, a:1.0}; 
    let {r:red, g:green, b:blue} = transparent;     // red=0.0, green=0.0, blue=0.0
    
    • 解构赋值可使用对象嵌套的语法。
    let data = {
        name: "destructuring assignment",
        type: "extension",
        impl: [{engine: "spidermonkey", version: 1.7},
               {engine: "rhino", version: 1.7}]
    };
    
    let ({name: feature, impl: [{engine:impl1, version: v1}, {engine:impl2}]} = data) {
        console.log(feature);   // => "destructuring assignment"
        console.log(impl1);     // => "spidermonkey"
        console.log(v1);        // => 1.7
        console.log(impl2);     // => "rhino"
    } 
    

    APIs to Support --build and --incremental

    TypeScript 3.6 增加了两组用于操作项目引用和增量式程序构建的 API。

    对于 --incremental,用户可以使用 createIncrementalProgram 和 createIncrementalCompilerHost API。用户还可以使用新的 readBuilderProgram 函数从该 API 生成的 .tsbuildinfo 文件中重新补充旧的程序实例,该函数仅用于创建新程序(即不能修改返回的实例,它仅用于其他 Create*Program 函数中的oldProgram 参数)。

    迭代

    New TypeScript Playground

    此版本中,新的 TypeScript playground 支持许多新的选择,包括:

    • target 选项(允许用户从 ES5 切换到 ES3、es2015、esnext 等)
    • 所有标记(包括 strict)
    • 对普通 JavaScript 文件的支持(使用 allowJS 和 CheckJS)

    当共享链接到 playground 示例时,这些选项也会持续存在,这样用户就可以更可靠地共享示例。

    for VS for/each

    for循环是遍历对象的属性,而for/each遍历对象属性的值

    a = ["one", "two", "three"];
    for(let p in a) console.log(p);     // => 0, 1, 2
    for each(let v in a) console.log(v) // => "one", "two", "three"
    

    Semicolon-Aware Code Edits

    像 Visual Studio 和 Visual Studio Code 这样的编辑器可以自动应用快速修复、重构和其他转换,比如从其他模块自动导入值。这些转换由 TypeScript 驱动,老版本的 TypeScript 无条件地在每条语句的末尾添加分号;不幸的是,这不符合许多用户的样式指南,许多用户对编辑器插入分号不满意。

    在此版本中,在应用这类编辑时,TypeScript 可以检测文件是否使用分号。如果你的文件是缺少分号,TypeScript 则不会添加分号。

    迭代器

    迭代器是一个对象,这个对象允许对它的值集合进行遍历,并保持任何必要的状态以便能够跟踪到当前遍历的"位置"。迭代器必须包含next()方法,每一次调用next()都返回集合中的下一个值。
    下面的counter()返回一个迭代器对象:

    function counter(start) {
        let nextValue = Math.round(start);
        return { next: function() { return nextValue++; }};     // 返回迭代器对象
    }
    
    let serialNumberGenerator = counter(1000);
    let sn1 = serialNumberGenerator.next();     // => 1000
    let sn2 = serialNumberGenerator.next();     // => 1001
    

    注:当迭代器用于有限的集合时,没有多余的值可迭代时,next()会抛出StopIterator,StopIterator是全局对象的属性,一个普通的对象(它自身没有属性),只是为了终结迭代而保留的一个对象。

    Smarter Auto-Imports

    JavaScript 有许多不同的模块语法或约定:ECMAScript 标准、Node 支持的模块语法或约定(CommonJS)、AMD、System.js 等等。在大多数情况下,TypeScript 将默认使用 ECMAScript 模块语法自动导入,在具有不同编译器设置的某些 TypeScript 项目中,或者在具有普通 JavaScript 和 Require 调用的 Node 项目中,这通常是不合适的。

    在 3.6 版本中,在决定如何自动导入其他模块之前,它会查看现有的导入。

    可迭代对象

    • 可迭代对象表示一组可迭代处理的值,可迭代对象必须包含一个_iterator_()的方法,用以返回这个集合的迭代器对象。
    • for/in循环会自动调用它的_iterator_()方法来获得一个迭代器对象(类型自动转换),然后调用迭代器的next()方法。for/in循环会自动处理StopIteration异常,而且处理过程对开发者是不可见的。
    function range(min, max) {
        return {
            get min() { return min; },
            get max() { return max; },
            includes:  function(x) { return min <= x && x <= max; },
            toString:  function()  { return "[" + min + "," + max + "]"; },
            _iterator_: function() {
                let val = Math.ceil(min);
                return { next: function() { 
                                    if(val > max) 
                                        throw StopIteration;
                                    return val++;
                                }
                        };
            }
        };
    }
    
    for(let i in range(1, 10)) console.log(i);  // => 输出1~10之间的数字
    

    Breaking Changes

    类成员 constructor 现在是构造函数

    根据 ECMAScript 规范,名为 constructor 的方法的类声明现在是构造函数,无论它们是使用标识符名称声明,还是使用字符串名称声明。

    图片 14

    图片 15

    DOM 更新

    在 lib.dom.d.ts 中已经删除或更改了许多声明,这包括(但不限于)以下内容:

    • 全局 window 不再定义为类型 Window,而是定义为类型 Window & type of globalThis。在某些情况下,将其类型称为 typeof window
    • GlobalFetch 移除了,而是使用 WindowOrWorkerGlobalScope
    • Navigator 上的某些非标准属性已经消失
    • experimental-webgl 移除了,而是使用 webgl 和 webgl2

    JSDoc 注解不再合并

    在 JavaScript 文件中,TypeScript 只会在紧接 JSDoc 注解之前确定声明的类型。

    图片 16

    关键字不能包含转义序列

    以前,关键字被允许包含转义序列。3.6 版本不再允许:

    图片 17

    关于 TypeScript 3.6 更多信息,请见 TypeScript 网站。使用 npm 命令获取:

    npm install -g typescript
    

    还可以通过以下方式获得编辑器支持:

    • 下载 Visual Studio 2019/2017
    • Visual Studio Code
    • Sublime Text 3 via PackageControl

    发布说明:

    (文/开源中国)    

    Iterator()函数

    • 如果这个函数的参数是一个可迭代对象,那么它将返回这个对象的_iterator_()方法的调用结果,返回一个迭代器对象。
    • 如果传入的对象或者数组没有定义_iterator_()方法,它会返回这个对象的一个可迭代的自定义迭代器。每次调用这个迭代器的next()方法都会返回包含2个值的数组,第1个数组元素是属性名,第2个数组元素是属性的值。
    for(let [k, v] in Iterator({a:1, b:2}))
        console.log(k + "=" + v);           // => "a=1", "b=2"
    
    • Iterator()函数返回的迭代器还有2个重要的特性。第一,它只对自有属性进行遍历而忽略继承的属性。第二,如果给Iterator()传入第2个参数true,返回的迭代器只对属性名进行遍历,而忽略属性值。
    o = {x:1, y:2};
    Object.prototype.z = 3;
    for(p in o) console.log(p);                 // => "x", "y", "z"
    for(p in Iterator(o)) console.log(p);       // => ["x", 1], ["y", 2],注:不会遍历继承属性
    for(p in Iterator(o, true)) console.log(p); // => "x", "y",          注:只遍历属性名
    

    生成器函数 Generator function

    任何使用关键字yield的函数都称为"生成器函数"。

    • yield和return的区别在于,使用yield的函数可保持函数内部状态的值。
    • 生成器函数通过yield返回值,不建议使用return返回值。
    • 和普通函数一样,生成器函数也通过关键字function声明,typeof运算符返回"function",并从Function.prototype继承属性和方法。
    • 对生成器函数的调用不会执行函数体,而是返回一个生成器对象
    • 如果不再使用生成器对象,可通过close()方法来释放它。调用close()相当于在函数运行挂起的位置执行了一条return语句,如果如果当前挂起的位置在try语句块中,那么将首先执行finally语句,再执行close()返回,如果finally语句块产生了异常,这个异常会传播给close()。

    下面是一个普通的生成器函数:

    function range(min, max) {
    
      for(let i=Math.ceil(min); i <= max; i++) {
        yield i;
      }
    
    }
    
    var f = range(3, 8);    // 返回一个生成器对象
    console.log(f.next());  // 3,返回单独值
    console.log(f.next());  // 4,返回单独值
    

    如果在生成器函数声明时加个"*",则yield会返回对象值:

    // function后添加"*"
    function *range(min, max) {
      for(let i=Math.ceil(min); i <= max; i++) {
        yield i;
      }
    
    }
    
    var f = range(3, 8);    // 返回一个生成器对象
    console.log(f.next());  // { value=3,  done=false,  z=3},返回对象
    console.log(f.next());  // { value=4,  done=false,  z=3},返回对象
    

    yield不仅可以返回值,还可以用于接收值,可通过next()或send()传递yield的接收值:

    function *test() {
      var i = yield 2;
    
      yield i;
    };
    
    var f = test();
    console.log(f.next());      // { value=2,  done=false,  z=3}, 第1次调用时,没有执行到yield语句,不需要接收值
    console.log(f.next(5));     // { value=5,  done=false,  z=3}, yield接收变量5,并赋值给i
    console.log(f.next(6));     // { done=true,  z=3,  value=undefined},最后一个值为undefined
    

    更多可参考:
    es6-generators
    如何理解ES6的yield

    数组推导 array comprehension

    数组推导是一种利用另外一个数组或可迭代对象来初始化数组元素的技术。
    数组推导的语法如下:

    [expression for ( variable in object ) if ( condition )]
    

    定义一个数组:

    let evensquares = [x*x for (x in range(0,10)) if (x % 2 === 0)]
    

    与下面的代码等价:

    let evensquares = [];
    for(x in range(0,10)) {
        if(x % 2 === 0)
            evensquares.push(x*x);
    }
    

    数组推导表达式特点:

    • 在变量之前没有关键字var和let,其实这里使用了隐式的let
    • if语句是可选的,如果省略的话,相当于给数组推导补充一条if(true)从句。

    生成器表达式 generator expression

    将数组推导中的方括号替换成圆括号,它就成了一个生成器表达式。比如:

    let h = (f(x) for(x in g));
    

    这段代码与下面的代码等价:

    function map(i, g) {
        for(let x in g) yield f(x);
    }
    

    函数简写

    如果函数只计算一个表达式并返回它的值,关键字return和花括号都可以省略。
    比如,对数组按降序排列:

    data.sort(function(a,b) b-a);   // 在"b-a"的前面省略了"{}"和return
    

    多catch从句

    try/catch语句中可以使用多catch从句,在catch从句的参数中可以添加关键字if进行异常类型判断。

    try {
        // 这里可能会抛出多种类型的异常
        throw 1;
    }
    catch(e if e instanceof ReferenceError) {
        // 这里处理引用错误
    }
    catch(e if e === "quit") {
        // 这里处理抛出的字符串是"quit"的情况
    }
    catch(e if typeof e === "string") {
        // 处理其他字符串的情况
    }
    catch(e) {
        // 处理余下的异常情况
    }
    finally {
        // finally从句正常执行
    }
    

    注:如果catch从句没有一个是true,那么程序会向上抛出这个未捕获的异常。

    E4X: ECMAScript for XML

    E4X为处理XML文档定义了一系列强大的特性。XML对象和原始的JavaScript对象不同,对它们进行typeof运算的结果是"xml"。XML对象和DOM对象没有任何关系

    XML对象创建

    • 直接量创建
    // 创建一个XML对象
    var pt = <periodictable>
                <element id="1"><name>Hydrogen</name></element>
                <element id="2"><name>Helium</name></element>
             </periodictable>;
    
    // 添加一个新元素
    pt.element += <element id="3"><name>Lithium</name></element>;
    
    • 构造函数构造
    // 创建单个节点 XML
    pt.element += new XML('<element id="4"><name>Boron</name></element>');
    
    // 创建多个节点 XMLList
    pt.element += new XMLList('<element id="4"><name>Boron</name></element>' + 
                              '<element id="5"><name>Lithium</name></element>');
    

    XML对象访问

    • 普通访问
    // 得到所有<element>标签的 列表
    var elements = pt.element;      
    
    // 得到所有的<name>标签的 列表
    var names = pt.element.name;    
    
    // "Hydrogen",name的第0个标签内容
    var n = names[0];               
    
    • 通配符(*)访问
    // 得到所有<element>标签的所有子节点(即<name>标签列表)
    var names = pt.element.*;
    
    • 使用字符@区分属性和标签名
    var pt = new XML('<periodictable><element id="1"><name>Hydrogen</name></element><element id="2"><name>Helium</name></element></periodictable>');
    
    // 访问id属性,输出"2"
    var a = pt.element[1].@id;
    
    // 获取所有的<element>标签的所有属性
    var b = pt.element.@*;
    
    • 过滤列表
    // 对所有的<element>元素组成的列表进行过滤
    // 过滤出那些id属性小于3的元素
    var c = pt.element.(@id < 3);
    
    • 删除标签和属性
    delete pt.element;          // 删除所有的<element>标签
    
    delete pt.element[0].@id;   // 删除一个属性
    

    命名空间

    E4X是支持命名空间的,它为使用XML命名空间提供了语法支持和API支持。

    // 声明默认的命名空间
    default xml namespace = "http://www.w3.org/1999/xhtml";
    

    本文由澳门新葡4473网站发布于热门贴子,转载请注明出处:TypeScript 3.6 发布,微软脚本编程语言

    关键词: