对一个具有枚举静态属性的类进行子类化,并保留类型信息。

14 浏览
0 Comments

对一个具有枚举静态属性的类进行子类化,并保留类型信息。

问题来源:将枚举定义为类的静态属性

在下面的代码中,我们有一个名为Box的类,其中定义了一个Color枚举属性,可以使用Box.Color引用该类型。

class Box {
  constructor(private color: Box.Color) {}
}
namespace Box {
  export enum Color {
    RED,
    GREEN,
    BLUE,
  }
}
function createBox (color: Box.Color): Box {
  return new Box(color);
}
const box = createBox(Box.Color.RED);
console.log(box); // { color: 0 }

我的问题是,当我尝试对Box进行子类化时,Color属性将会继承(在JavaScript中),但类型信息在TypeScript中不会继承。

class SmallBox extends Box { }
function createSmallBox (color: SmallBox.Color): SmallBox {
  //                            ^ Error
  return new SmallBox(color);
}
const box = createSmallBox(SmallBox.Color.RED);
//                         ^ Works
console.log(box);

这在JavaScript中是可行的,因为SmallBox继承了Box的静态Color属性。然而,在TypeScript中不起作用,因为类型信息丢失了。是否有一种方法可以在保留类型信息的同时实现相同的效果?我正在寻找一种通用的方式,可以将Box的所有类型信息应用于SmallBox,而无需创建一个新的SmallBox命名空间并手动列出每个类型。


编辑:我已经能够提出以下解决方案,它需要一个额外的命名空间来传递信息。不过,如果可能的话,我希望摆脱额外的子命名空间。

class Box {
  constructor(private color: Box.Subtypes.Color) {}
}
namespace Box {
  export namespace Subtypes {
    export enum Color {
      RED,
      GREEN,
      BLUE,
    }
  }
}
function createBox (color: Box.Subtypes.Color): Box {
  return new Box(color);
}
class SmallBox extends Box { }
namespace SmallBox {
  export import Subtypes = Box.Subtypes;
}
function createSmallBox (color: SmallBox.Subtypes.Color): SmallBox {
  //                            ^ Works
  return new SmallBox(color);
}
const box = createSmallBox(SmallBox.Subtypes.Color.RED);
//                         ^ Works
console.log(box);

0
0 Comments

问题的原因是希望在子类中继承父类的枚举静态属性,并保持类型信息。当前的解决方法是通过在基类上定义一个具有所需枚举值的对象,并使用as const(一个const断言)来创建一个类型别名来表示这些枚举值的联合(在下面的示例中为BoxColor)。使用这种方法,一切都更简单:没有命名空间合并,没有枚举代码生成等。

解决方法的代码示例:

type Values<T> = T[keyof T];

export type BoxColor = Values<typeof Box.Color>; // 0 | 1 | 2

class Box {

static readonly Color = {

RED: 0,

GREEN: 1,

BLUE: 2,

} as const;

constructor(private color: BoxColor) {}

}

function createBox (color: BoxColor): Box {

return new Box(color);

}

const box = createBox(Box.Color.RED);

console.log(box); // { color: 0 }

export class SmallBox extends Box {}

export function createSmallBox (color: BoxColor): SmallBox {

return new SmallBox(color);

}

const smallBox = createSmallBox(SmallBox.Color.RED);

console.log(smallBox); // { color: 0 }

如果消费者需要访问该类型,他们可以在值导出的同时导入它:

import {type BoxColor, createSmallBox} from './the-module-above.mts';

然而,这种解决方法并不能解决问题。虽然可以导出多个内容,包括类型,然后导入这些内容并使用它们。但问题是我想仅导入SmallBox并获得所有与它相关的内容:包括SmallBox类(可以),SmallBox类型(可以),SmallBox.Color对象(可以),以及SmallBox.Color类型(不行)。为什么有一种方法可以让值和类型重叠,但没有一种方法可以让对象属性的值和类型重叠,并且能够继承,我不确定。

感谢您提供有关const断言的信息!但如果这些内容是标准,为什么你在问题中没有包含它们呢?

0