首页 > 解决方案 > 在运行时区分泛型类型的可能性的最佳方法是什么?

问题描述

我有一个通用类型,它可以是 6 种可能性之一:

type Attribute<T> = {
  name: string;
  value: T | null;
};

type StringAttribute = Attribute<string>;
type BoolAttribute = Attribute<boolean>;
type NumberAttribute = Attribute<number>;
type NumberArrayAttribute = Attribute<number[]>;
type PointAttribute = Attribute<Point>;
type PointArrayAttribute = Attribute<Point[]>;

我想在运行时区分这些。在伪代码中:

[..attributes].forEach((attr) => setAttribute(attr, target))

function setAttribute(attr: Attribute<string | boolean | number | number[] | point | point[]>, target: ExtendedElement) {
  ...
  if (isNumberArrayAttribute(attr)) {
   target.setNumberArrayAttribute(attr.name, attr.value)
  }
  ...
}

由于无法在正常运行时检查 TypeScript 类型,因此我需要编写一些 JavaScript 代码来模仿 TypeScript 类型。我看到了两种替代解决方案

用户定义的类型保护:

function isNumberArray(val: any): val is number[] {
  if (!val.isArray()) {
    return false;
  }
  return val.every((v) => typeof v === "number")
} 

function setAttribute(attr: Attribute<string | boolean | number | number[] | point | point[]>, target: ExtendedElement) {
  ...
  if (isNumberArray(attr.value)) {
    target.setAttr(attr.name, attr.value)
  }
  ...
}

课程:

class NumberArrayAttribute extends Attribute {
  name: string
  value: number[]
  constructor(name: string, value: number[]) {
    this.name = name;
    this.value= value;
  }
}

function setAttribute(attr: Attribute<string | boolean | number | number[] | point | point[]>, target: ExtendedElement) {
  ...
  if (attr instanceof NumberArrayAttribute) {
    target.setNumberArrayAttribute(attr.name, attr.value)
  }
  ...
}

对于我的问题:

标签: typescript

解决方案


为什么要在函数setAttribute中区分泛型类型?如果这样做,每次添加新属性时,您还需要更新函数setAttribute

在我看来,每个属性实例都应该知道如何设置“属性”。

考虑这个接口:

interface Attribute<T> {
  name: string;
  value: T | null;
  guard(): boolean;
  setAttribute(target: ExtendedElement): void;
};

和这个类:

class NumberArrayAttribute implements Attribute<number[]> {
  name: string;
  value: number[];

  constructor(name: string, value: number[]) {
    this.name = name;
    this.value= value;
  }

  public guard(): boolean {
    return this.value.isArray() && this.value.every((v) => typeof v === "number");
  }

  public setAttribute(target: ExtendedElement): void {
    target.setAttribute(this.name, this.value)
  }
}

该类知道如何执行守卫setAttribute。现在您可以根据需要创建任意数量的 Attribute<> 类,而无需更改函数 setAttribute:

function setAttribute(){
   [...attributes].forEach((attr: Attribute<any>) => {
       if (attr.guard()){
          attr.setAttribute(target)
       }
   })
}

推荐阅读