导入属性
TypeScript 5.3支持导入属性提案的最新更新。
导入属性的一个用例是向运行库提供有关模块预期格式的信息。
代码语言:javascript复制 // We only want this to be interpreted as JSON,
// not a runnable/malicious JavaScript file with a `.json` extension.
import obj from "./something.json" with { type: "json" };
这些属性的内容不会被TypeScript检查,因为它们是特定于主机的,并且只是单独留下,以便浏览器和运行时可以处理它们(可能是错误)。
代码语言:javascript复制 // TypeScript is fine with this.
// But your browser? Probably not.
import * as foo from "./foo.js" with { type: "fluffy bunny" };
动态import()
调用也可以通过第二个参数使用导入属性。
const obj = await import("./something.json", {
with: { type: "json" }
});
第二个参数的预期类型由一个名为ImportCallOptions
的类型定义,默认情况下,该类型只需要一个名为with
的属性。
请注意,导入属性是早期称为“导入断言”的提案的演变 最明显的区别是使用with
关键字而不是assert
关键字。 但不太明显的区别是,运行时现在可以自由地使用属性来指导导入路径的解析和解释,而导入断言只能在加载模块后断言某些特性。
随着时间的推移,TypeScript将弃用旧的导入断言语法,而支持拟议的导入属性语法。 使用assert
的现有代码应该迁移到with
关键字。 需要导入属性的新代码应该独占地使用with
。
我们 我们
稳定支持 resolution-mode
导入类型
在TypeScript 4.7中,TypeScript在resolution-mode
中添加了对/// <reference types="..." />
属性的支持,以控制是否应该通过import
或require
语义解析说明符。
/// <reference types="pkg" resolution-mode="require" />
// or
/// <reference types="pkg" resolution-mode="import" />
一个相应的字段也被添加到纯类型导入的导入断言中; 然而,它只在TypeScript的夜间版本中得到支持。 理由是,在精神上,导入断言并不打算指导模块解析。 因此,该功能在夜间模式下进行了试验,以获得更多的反馈。
但是考虑到import属性可以指导解析,并且我们已经看到了合理的用例,TypeScript 5.3现在支持resolution-mode
的import type
属性。
// Resolve `pkg` as if we were importing with a `require()`
import type { TypeFromRequire } from "pkg" with {
"resolution-mode": "require"
};
// Resolve `pkg` as if we were importing with an `import`
import type { TypeFromImport } from "pkg" with {
"resolution-mode": "import"
};
export interface MergedType extends TypeFromRequire, TypeFromImport {}
这些导入属性也可以用于import()
类型。
export type TypeFromRequire =
import("pkg", { with: { "resolution-mode": "require" } }).TypeFromRequire;
export type TypeFromImport =
import("pkg", { with: { "resolution-mode": "import" } }).TypeFromImport;
export interface MergedType extends TypeFromRequire, TypeFromImport {}
欲了解更多信息,请查看此处的更改
resolution-mode
支持所有模块模式
以前,仅允许在resolution-mode
选项moduleResolution
和node16
下使用nodenext
。 为了更容易地查找专门用于类型目的的模块,resolution-mode
现在可以在所有其他moduleResolution
选项(如bundler
,node10
)中正常工作,并且在classic
下不会出错。
有关详细信息,请参阅实现pull request。
switch (true)
缩小
TypeScript 5.3现在可以基于case
中每个switch (true)
子句中的条件执行收缩。
function f(x: unknown) {
switch (true) {
case typeof x === "string":
// 'x' is a 'string' here
console.log(x.toUpperCase());
// falls through...
case Array.isArray(x):
// 'x' is a 'string | any[]' here.
console.log(x.length);
// falls through...
default:
// 'x' is 'unknown' here.
// ...
}
}
这个功能是由Mateusz Burzyovski率先开始的 我们想表达一个“谢谢”对于这种贡献。
缩小与布尔值比较的范围
有时候,你可能会发现自己在一个条件下与true
或false
进行直接比较。 通常这些都是不必要的比较,但您可能更喜欢它作为一种风格,或者避免围绕JavaScript真实性的某些问题。 无论如何,以前的TypeScript在执行收缩时无法识别这样的表单。
TypeScript 5.3现在在缩小变量时保持并理解这些表达式。
代码语言:javascript复制 interface A {
a: string;
}
interface B {
b: string;
}
type MyType = A | B;
function isA(x: MyType): x is A {
return "a" in x;
}
function someFn(x: MyType) {
if (isA(x) === true) {
console.log(x.a); // works!
}
}
我们
instanceof
缩小范围 Symbol.hasInstance
JavaScript的一个稍微深奥的特性是可以覆盖instanceof
操作符的行为。 为此,instanceof
操作符右侧的值需要有一个名为Symbol.hasInstance
的特定方法。
class Weirdo {
static [Symbol.hasInstance](testedValue) {
// wait, what?
return testedValue === undefined;
}
}
// false
console.log(new Thing() instanceof Weirdo);
// true
console.log(undefined instanceof Weirdo);
为了在instanceof
中更好地建模这种行为,TypeScript现在检查是否存在这样的[Symbol.hasInstance]
方法,并将其声明为类型谓词函数。 如果是,则instanceof
操作符左侧的测试值将由该类型谓词适当地缩小。
interface PointLike {
x: number;
y: number;
}
class Point implements PointLike {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
distanceFromOrigin() {
return Math.sqrt(this.x ** 2 this.y ** 2);
}
static [Symbol.hasInstance](val: unknown): val is PointLike {
return !!val && typeof val === "object" &&
"x" in val && "y" in val &&
typeof val.x === "number" &&
typeof val.y === "number";
}
}
function f(value: unknown) {
if (value instanceof Point) {
// Can access both of these - correct!
value.x;
value.y;
// Can't access this - we have a 'PointLike',
// but we don't *actually* have a 'Point'.
value.distanceFromOrigin();
}
}
正如你在这个例子中看到的,Point
定义了自己的[Symbol.hasInstance]
方法。 它实际上充当了一个名为PointLike
的单独类型的自定义类型保护。 在函数f
中,我们能够用value
将PointLike
缩小到instanceof
,但不能缩小到Point
。 这意味着我们可以访问属性x
和y
,但不能访问方法distanceFromOrigin
。
有关更多信息,您可以在这里阅读有关此更改的信息。
检查 super
实例字段上的属性访问
在JavaScript中,可以通过super
关键字访问基类中的声明。
class Base {
someMethod() {
console.log("Base method called!");
}
}
class Derived extends Base {
someMethod() {
console.log("Derived method called!");
super.someMethod();
}
}
new Derived().someMethod();
// Prints:
// Derived method called!
// Base method called!
这与编写类似this.someMethod()
的代码不同,因为这可能会调用重写的方法。 这是一个微妙的区别,更微妙的是,如果一个声明从来没有被重写过,这两者通常可以互换。
class Base {
someMethod() {
console.log("someMethod called!");
}
}
class Derived extends Base {
someOtherMethod() {
// These act identically.
this.someMethod();
super.someMethod();
}
}
new Derived().someOtherMethod();
// Prints:
// someMethod called!
// someMethod called!
问题是它们可以互换使用,因为super
只对在原型上声明的成员有效,而不是实例属性。 这意味着如果你写了super.someMethod()
,但是someMethod
被定义为一个字段,你会得到一个运行时错误!
class Base {
someMethod = () => {
console.log("someMethod called!");
}
}
class Derived extends Base {
someOtherMethod() {
super.someMethod();
}
}
new Derived().someOtherMethod();
//