相关文章导航
- Typescript 3.7 常用新特性一览
- Typescript 3.8 常用新特性一览
- Typescript 3.9 常用新特性一览
- Typescript 4.0 beta 常用新特性一览
写在最前面
3.8
添加了几个有趣的特性- 对 ECMAScript 的模块部分做了优化
#
私有字段的使用来替代不太严格的private
等。- 顶层
await
的使用; - 这里只翻译了部分重要的特性,还有更多好玩的新特性可以看下面的参考链接原文查看。
1、类型限制的导入导出方法 (Type-Only Imports and Export)
TypeScript 3.8为仅类型导入和导出添加了新语法。
代码语言:javascript复制import type { SomeThing } from "./some-module.js";
export type { SomeThing };
- 大家可以在
playground
上试一试,这样导入和导出,ts 是不会解析,这个特性不常用,如果你子配置如下问题的时候可能遇到 - 如果您在--
isolatedModulesTypeScript
的transpileModuleAPI
或Babel
下遇到问题,则此功能可能是相关的。
代码语言:javascript复制使用这样的导入的新类型也是不支持扩展等方法的
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.
// ...
}
复制代码
2、ECMAScript 提案的私有字段(ECMAScript Private Fields)
2.1 Private Fields 的基本特性
- 这是 Stage-3类字段提案的一部分。
- 在
typescript
中我们可以这么使用私有字段
了
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)拥有下面这些特性。
- 专用字段以
#
字符开头。有时我们称这些prviate name
。 - 每个专用字段名称都唯一地限定于其包含的类。
TypeScript
辅助功能修饰符,例如public
,private
不能在私有字段上使用。- 私有字段包含的类之外被检测到,我们称这种为严格的隐私模式。
2.2 Private Fields 的使用规范
除了能保存自己的私有这一属性以外,私有字段的另一个好处是我们刚才提到的唯一性。例如,常规属性声明易于在子类中被覆盖。而 private fields
是受保护的。
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!!
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
修饰符啦?
当涉及到属性时,TypeScript
的private
修饰符会并没有完全正确的执行,它的行为完全像普通属性一样,并且没有办法告诉它是使用private
修饰符并没有完全的生效。我们称之为 soft privacy
, 我们依然可以通过 ['foo']
这样的形式访问到。看下面的代码:
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 的时候解决兼容的问题,但是这种方法不太严格。
- 对比下面使用
#
私有字段,是完全访问不到的
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'.
复制代码
代码语言:javascript复制结论就是,如果你想严格的保护您的私有属性的值,就使用 `#` 即可,子类继承的时候也无需担心命名冲突的问题。当我们还是使用 `private` 的时候就需要注意对私有修饰符的定义的值修改的问题了。
3、 export * as ns 语法使用
typescript 也支持这种用法啦,在导入模块的 as 重新定义模块名的模块的时候,我们可以重新导出到单独模块名。
// menu.ts
代码语言:javascript复制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
代码语言:javascript复制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
是导出全部模块。
- Q&A stackoverflow
- MDN-import
4、顶层 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 {};
代码语言:javascript复制请注意,这里有一个微妙之处:顶层await仅在模块的顶层起作用,并且只有当TypeScript找到一个真正可用的模块才允许使用,我们可以用一个
export {}
来检测是否在模块下使用。
这个方法还在实验中不建议使用,等 3.9 吧
参考
- www.typescriptlang.org/docs/handbo…