【译】Typescript 3.8 经常使用新特性一览

写在最前面

  • 3.8 添加了几个有趣的特性,对 ECMAScript 的模块部分作了优化, # 私有字段的使用来替代不太严格的 private等。

一、类型限制的导入导出方法 (Type-Only Imports and Export)

TypeScript 3.8为仅类型导入和导出添加了新语法。html

import type { SomeThing } from "./some-module.js";

export type { SomeThing };
复制代码
  • 你们能够在 playground 上试一试,这样导入和导出,ts 是不会解析,这个特性不经常使用,若是你子配置以下问题的时候可能遇到
  • 若是您在--isolatedModulesTypeScripttranspileModuleAPIBabel 下遇到问题,则此功能多是相关的。

使用这样的导入的新类型也是不支持扩展等方法的react

import type { Component } from "react";

interface ButtonProps {
    // ...
}

class Button extends Component<ButtonProps> {
    // ~~~~~~~~~
    // error! 'Component' only refers to a type, but is being used as a value here.

    // ...
}
复制代码

二、ECMAScript 提案的私有字段(ECMAScript Private Fields)

2.1 Private Fields 的基本特性

class Person {
    #name: string

    constructor(name: string) {
        this.#name = name;
    }

    greet() {
        console.log(`Hello, my name is ${this.#name}!`);
    }
}

let jeremy = new Person("Jeremy Bearimy");

jeremy.#name
// ~~~~~
// Property '#name' is not accessible outside class 'Person'
// because it has a private identifier.
复制代码

!!! 和常规属性(这里特别比较 private 修饰符声明的比较)不一样,私有字段(private fields)拥有下面这些特性。git

  • 专用字段以 # 字符开头。有时咱们称这些 prviate name
  • 每一个专用字段名称都惟一地限定于其包含的类。
  • TypeScript 辅助功能修饰符,例如 publicprivate 不能在私有字段上使用。
  • 私有字段甚至在JS用户以外都没法访问,甚至没法在包含的类以外被检测到!有时咱们称这种严格的隐私。

2.2 Private Fields 的使用规范

除了能保存本身的私有这一属性之外,私有字段的另外一个好处是咱们刚才提到的惟一性。例如,常规属性声明易于在子类中被覆盖。而 private fields 是受保护的。github

class C {
    foo = 10;

    cHelper() {
        return this.foo;
    }
}

class D extends C {
    foo = 20;

    dHelper() {
        return this.foo;
    }
}

let instance = new D();
// 'this.foo' refers to the same property on each instance.
console.log(instance.cHelper()); // prints '20'
console.log(instance.dHelper()); // prints '20'
复制代码

访问任何其余类型的私有字段都将致使 TypeError!!typescript

class Square {
    #sideLength: number;

    constructor(sideLength: number) {
        this.#sideLength = sideLength;
    }

    equals(other: any) {
        return this.#sideLength === other.#sideLength;
    }
}

const a = new Square(100);
const b = { sideLength: 100 };

// Boom!
// TypeError: attempted to get private field on non-instance
// This fails because 'b' is not an instance of 'Square'.
console.log(a.equals(b));
复制代码

2.3 那咱们到底该使用 # 定制的私有字段仍是使用 private 修饰符啦?

当涉及到属性时,TypeScriptprivate修饰符会并无彻底正确的执行,它的行为彻底像普通属性同样,而且没有办法告诉它是使用private 修饰符并无彻底的生效。咱们称之为 soft privacy, 咱们依然能够经过 ['foo'] 这样的形式访问到。看下面的代码:api

class C {
    private foo = 10;
}

// This is an error at compile time,
// but when TypeScript outputs .js files,
// it'll run fine and print '10'.
console.log(new C().foo);    // prints '10'
// ~~~
// error! Property 'foo' is private and only accessible within class 'C'.

// TypeScript allows this at compile-time
// as a "work-around" to avoid the error.
console.log(new C()["foo"]); // prints '10'
复制代码

好处固然是帮助您在使用一些 api 的时候解决兼容的问题,可是这种方法不太严格。async

  • 对比下面使用 # 私有字段,是彻底访问不到的
class C {
    #foo = 10;
}

console.log(new C().#foo); // SyntaxError
// ~~~~
// TypeScript reports an error *and*
// this won't work at runtime!

console.log(new C()["#foo"]); // prints undefined
// ~~~~~~~~~~~~~~~
// TypeScript reports an error under 'noImplicitAny',
// and this prints 'undefined'.
复制代码

结论就是,若是你想严格的保护您的私有属性的值,就使用 `#` 便可,子类继承的时候也无需担忧命名冲突的问题。当咱们仍是使用 `private` 的时候就须要注意对私有修饰符的定义的值修改的问题了。 ide

三、 export * as ns 语法使用

typescript 也支持这种用法啦,在导入模块的 as 从新定义模块名的模块的时候,咱们能够从新导出到单独模块名。函数

// menu.tsfetch

export const MENU1 = __('nav: 菜单 1');
export const MENU2 = __('nav: 菜单 2');
export const MENU3 = __('nav: 菜单 3');
export const MENU4 = __('nav: 菜单 4');
export const DEMO = __('nav:Demo');

复制代码

// module.ts

import * as menu from "./menu.ts";
console.log(menu.MENU1) // 'nav: 菜单 1';
console.log(menu.MENU2) // 'nav: 菜单 2';
// ....

export { menu };
复制代码

ps: ECMAScript 2020 最近也添加了这种语法!

3.1 疑问?

import * as React from 'react'; vs import React from 'react'; 这两个使用有什么区别吗?

import * as React from 'react';
...or like this:

import React from 'react';
复制代码

The first imports everything in the react module (see: Import an entire module's contents)

The second imports only the default module export (see: Importing defaults)

简而言之就是咱们使用的 import React from 'react' 实际上是导出的默认的模块,而用到 * as 是导出所有模块。

四、顶层 await 使用

  • 一般咱们使用 JavaScript 常会引入一个async 函数来使用 await.
  • JavaScript中(以及其余大多数具备相似功能的语言)await 仅在 async 函数体内被容许。可是,对于 top await,咱们能够 await 在模块的顶层使用。
async function main() {
    const response = await fetch("...");
    const greeting = await response.text();
    console.log(greeting);
}

main()
    .catch(e => console.error(e))
复制代码
  • 具体的模块参加以下:
const response = await fetch("...");
const greeting = await response.text();
console.log(greeting);

// Make sure we're a module
export {};
复制代码

请注意,这里有一个微妙之处:顶层await仅在模块的顶层起做用,而且只有当TypeScript找到一个真正可用的模块才容许使用,咱们能够用一个 export {} 来检测是否在模块下使用。

这个方法还在实验中不建议使用,等 3.9 吧

参考

相关文章
相关标签/搜索