TypeScript 5.3 来了,一大波新特性( 二 )


function f(x: unknown) {switch (true) {case typeof x === "string":// 这里,'x' 是一个 'string'console.log(x.toUpperCase());case Array.isArray(x):// 这里 'x' 是一个 'string | any[]'console.log(x.length);default:// 这里 'x' 是 'unknown'// ...}}布尔值比较有时候你可能会在条件语句中直接与 true 或 false 进行比较 。通常这些比较是不必要的,但可能出于代码风格或避免 JavaScript 真值方面的某些问题而偏好这种写法 。然而,在之前的 TypeScript 版本中,它不能正确地识别这种形式来执行类型缩小 。
【TypeScript 5.3 来了,一大波新特性】TypeScript 5.3 现在在缩小变量范围时可以跟上并理解这些表达式 。
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!}}Symbol.hasInstanceJavaScript 的一个特性是可以重写 instanceof 运算符的行为 。要实现这一点,需要在 instanceof 运算符右侧的值上定义一个名为 Symbol.hasInstance 的特定方法 。
class Weirdo {static [Symbol.hasInstance](testedValue) {return testedValue =https://www.isolves.com/it/cxkf/bk/2023-11-16/== undefined;}}// falseconsole.log(new Thing() instanceof Weirdo);// trueconsole.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) {// 可以访问这两个属性 - 正确!value.x;value.y;// 无法访问这个属性 , 有 'PointLike' 类型的对象,但实际上并没有 'Point' 类型的对象 。value.distanceFromOrigin();}}在这个例子中,Point 定义了自己的 [Symbol.hasInstance]方法 。它实际上充当了一个对称为 PointLike 的独立类型的自定义类型保护程序 。在函数 f 中,我们能够通过 instanceof 将 value 缩小到 PointLike 类型 , 但无法缩小到 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();// 输出结果://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();// 输出结果://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();TypeScript 5.3 现在更仔细地检查 super 属性访问/方法调用,以查看它们是否对应于类字段 。如果是的话,现在会得到一个类型检查错误 。
嵌入提示支持跳转到类型的定义TypeScript 的嵌入提示现在支持跳转到类型的定义!

TypeScript 5.3 来了,一大波新特性

文章插图
按住 Ctrl 键单击嵌入提示可跳转至参数类型的定义 。


推荐阅读