原文链接 Announcing TypeScript 3.8 by Daniel,本文参考翻译了部分原文
TypeScript近期更新了3.8版本,带来了许多新特性
1. 类型导入和导出(Type-Only Imports and Exports)
这个特性可能平时大家基本用不到,但是如果碰到过类似问题的话大家可能对这个会感兴趣一些(尤其是在--iosolateModules下变编译,或者使用transplieModule API, 或者Babel)
问题出现的原因是Typescript在引用类型的时候复用了Javascript的import语法。
// ./foo.ts
interface Options {
// ...
}
export function doThing(options: Options) {
// ...
}
// ./bar.ts
import { doThing, Options } from "./foo.js";
function doThingBetter(options: Options) {
// do something twice as good
doThing(options);
doThing(options);
}
这样导入doThing和Options很方便,因为大多数情况下我们不关心导入的具体内容,只知道导入了一些东西。不幸的是代码可以正常运行是因为一个被称为导入省略(import elision)的特性,它发现Options是一个类型,然后自动去掉了它的导入。真正的执行结果是:
// ./foo.js
export function doThing(options: Options) {
// ...
}
// ./bar.js
import { doThing } from "./foo.js";
function doThingBetter(options: Options) {
// do something twice as good
doThing(options);
doThing(options);
}
也许这也不能引起你的注意,但是当代码是以下这种情况的时候会出现混乱
import { MyThing } from "./some-module.js";
export { MyThing };
我们根本无法确定MyThing是否是一个类型
为了避免出现类似情况,TypeScript3.8 添加了一个新的类型导入和导出的语法,如下:
import type { SomeThing } from "./some-module.js";
export type { SomeThing };
需要注意的是import type 只是为了注释和申明,它会被删除,在运行时无任何意义
2. ECMAScript 私有变量(ECMAScript Private Fields)
Typescript3.8 提供了ECMAScript私有属性(ECMAScript’s private fields),这个特性是stage-3 class fields proposal的一部分。
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
// ~~~~~
// 属性 '#name' 不能在类Person 外访问
// 因为它有一个私有属性的标识符
ECMAScript私有属性和普通的私有属性的异同:
- 私有属性以字符 # 开头
- 每一个私有属性名在包含它的类里面有唯一作用域
- Typescript 访问修饰符,例如public 和private 不能用于私有属性上
- 私有属性不能在它所属类的外边访问或者检测到-即使是JS代码本人,我们称之为强隐私(hard privacy)
对于第二条举例,对于普通的属性在子类种重复申明,父类的中的属性会被覆盖。
class C {
foo = 10;
cHelper() {
return this.foo;
}
}
class D extends C {
foo = 20;
dHelper() {
return this.foo;
}
}
let instance = new D();
// 'this.foo' 在每个实例中都引入了相同的属性
console.log(instance.cHelper()); // prints '20'
console.log(instance.dHelper()); // prints '20'
使用私有属性后:
class C {
#foo = 10;
cHelper() {
return this.#foo;
}
}
class D extends C {
#foo = 20;
dHelper() {
return this.#foo;
}
}
let instance = new D();
// 'this.#foo' 对于每个类都引入了不同的属性
console.log(instance.cHelper()); // prints '10'
console.log(instance.dHelper()); // prints '20'
private关键字 VS ECMAScript 私有属性
- private在运行时会被全部删除,并且private只在编译时有效,而私有属性在类外完全无法访问
- 私有属性不用担心类继承中的属性名冲突问题
- private关键字修饰的属性和其他属性没什么不同,在运行时都可以快速访问,但是私有属性依赖于WeakMaps 会降低属性访问速度
3. 顶层await(Top-Level await)
以前的await 关键字只能出现在async函数内部:
async function main() {
const response = await fetch("...");
const greeting = await response.text();
console.log(greeting);
}
main()
.catch(e => console.error(e))
当我们希望一开始就引入的时候往往使用立即执行函数去处理
await Promise.resolve(console.log('🎉'));
// → SyntaxError: await is only valid in async function
(async function() {
await Promise.resolve(console.log('🎉'));
// → 🎉
}());
为了避免引入async函数,提供了一个称为顶层 await的特性
const response = await fetch("...");
const greeting = await response.text();
console.log(greeting);
// Make sure we're a module
export {};
通过顶层await我们可以有以下几种用法:
const strings = await import(`/i18n/${navigator.language}`); // 动态引入路径
const connection = await dbConnector(); // 资源初始化
let jQuery; // 依赖回退
try {
jQuery = await import('https://cdn-a.example.com/jQuery');
} catch {
jQuery = await import('https://cdn-b.example.com/jQuery');
}
4. 其他特性
- export * as ns Syntax
- JSDoc Property Modifiers
- Better Directory Watching on Linux and watchOptions
- Fast and Loose” Incremental Checking
- Editor Features(onvert to Template String)
- Editor Features(Call Hierarchy)
- Breaking Changes
参考文章: