代码之家  ›  专栏  ›  技术社区  ›  Rostys

TypeScript:使用类型转换进行类型保护

  •  1
  • Rostys  · 技术社区  · 1 年前

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

    我已经看到使用 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
            }
        }
    }
    

    我应该换一种方式吗?如果是,你认为哪种方式更好?

    2 回复  |  直到 1 年前
        1
  •  2
  •   jcalz    1 年前

    您可以使用 in operator narrowing 检查是否存在 "angle" 在里面 init 和狭窄 初始化 至所需 union member 不需要 type assertions (您称之为“选角”):

    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 link to code

        2
  •  1
  •   ColaFanta    1 年前

    你需要的东西叫 Type Predicate 这是缩小工会类型的官方方式。它是一个具有特殊返回类型的函数 x is SomeType 它实际上是一个布尔值,但通知 typescript 编译器,您实际需要做的是断言类型,而不仅仅是返回布尔值。

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

    首先定义您的 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  
        }
      }
    }