TypeScript:对 typeguard 使用类型转换

TypeScript: Using typecasting for typeguard

提问人:Rostys 提问时间:9/1/2023 最后编辑:Rostys 更新时间:9/1/2023 访问量:44

问:

我有带有构造函数的类,可以接受两种类型的对象,然后根据它得到的对象类型,执行不同的操作。

我已经看到可以使用关键字。 但我觉得不对劲。as

export type Angle = {
    cos: number; 
    sin: number
}
export type VectorAngleInit = {
    angle: Angle,
    magnitude: number
}
export type VectorDInit = {
    dx: number,
    dy: number
}
export class Vector {
    #dx: number;
    #dy: number;
    constructor(init: VectorAngleInit | VectorDInit) {
        if ((init as VectorAngleInit)?.angle) {
            const angleInit = init as VectorAngleInit
            this.#dx = angleInit.magnitude * angleInit.angle.cos;
            this.#dy = angleInit.magnitude * angleInit.angle.sin;
        } else {
            const dInit = init as VectorDInit
            this.#dx = dInit.dx
            this.#dy = dInit.dy
        }
    }
}

我应该以另一种方式做吗?如果是这样,你认为哪种方式更好?

JavaScript TypeScript 强制转换 TypeGuard

评论


答:

2赞 jcalz 9/1/2023 #1

您可以使用 in 运算符缩小来检查是否存在 in 并将范围缩小到所需的联合成员,而无需类型断言(您称之为“强制转换”):"angle"initinit

export class Vector {
  #dx: number;
  #dy: number;
  constructor(init: VectorAngleInit | VectorDInit) {
    if ("angle" in init) {
      this.#dx = init.magnitude * init.angle.cos;
      this.#dy = init.magnitude * init.angle.sin;
    } else {
      this.#dx = init.dx
      this.#dy = init.dy
    }
  }
}

Playground 代码链接

2赞 ColaFanta 9/1/2023 #2

您需要的东西称为类型谓词,这是缩小联合类型范围的官方方法。它是一个具有特殊返回类型的函数,该返回类型实际上是一个布尔值,但通知编译器您实际需要做的是谓词该类型,而不仅仅是返回布尔值。x is SomeTypetypescript

与您的问题相关的示例可能是这样的:

首先定义函数type predicate

export type VectorAngleInit = {
  angle: Angle
  magnitude: number
}

// type predicate predicating whether u is of type VectorAngleInit
export function isVectorAngleInit(u: unknown): u is VectorAngleInit {
  return !!u && typeof u === 'object' && (u as VectorAngleInit).angle instanceof Angle
}

export type VectorDInit = {
  dx: number
  dy: number
}

// type predicate predicating whether u is of type VectorDInit
export function isVectorDInit(u: unknown): u is VectorDInit {
  return !!u && typeof u === 'object' && typeof (u as VectorDInit).dx === 'number'
}

然后在构造函数中使用它:

export class Vector {
  #dx: number
  #dy: number
  constructor(init: VectorAngleInit | VectorDInit) {
    if (isVectorAngleInit(init)) {
      // notice that type of `init` is auto inferred within if block
      this.#dx = init.magnitude * init.angle.cos
      this.#dy = init.magnitude * init.angle.sin  
    }
    if(isVectorDInit(init)){
      this.#dx = init.dx
      this.#dy = init.dy  
    }
  }
}