使用类名来命名接口属性

huangapple go评论143阅读模式
英文:

Use class name to name interface properties

问题

I want to use Class.name as a variable to name my properties.

class MyClass {
  readonly a: {b: number} = { b: 123 };

  constructor() {
    this.a.b = 1;
  }
}

// try1
interface Actions {
  [MyClass.name + '_action1']: string,
  [MyClass.name + '_action2']: number
}

interface Actions2 {
  MyClass_action1: string,
  MyClass_action2: number
}

// expect a and b to behave the same
const a: Actions2 = {
  MyClass_action1: '4',
  MyClass_action2: 1
}

const b: Actions = {
  MyClass_action1: '4',
  MyClass_action2: 1
}

Is this even possible?

英文:

I think this TS Playground shows what Im trying to achieve:

I want to use Class.name as a variable to name my properties.

class MyClass {
  readonly a: {b: number} = { b: 123 };

  constructor() {
    this.a.b = 1;
  }
}

// try1
interface Actions {
  [MyClass.name + '_action1']: string,
  [MyClass.name + '_action2']: number
}

interface Actions2 {
  MyClass_action1: string,
  MyClass_action2: number
}

// expect a and b to behave the same
const a: Actions2 = {
  MyClass_action1: '4',
  MyClass_action2: 1
}

const b: Actions = {
  MyClass_action1: '4',
  MyClass_action2: 1
}

Is this even possible?

答案1

得分: 1

这是没有解决方法的。

类构造函数的 name 属性的类型是 string,继承自 Function 接口。编译器不知道 namestring 字面类型。请参见 microsoft/TypeScript#43325 以及相关的问题。

假设我们确切地知道在运行时,class Foo {⋯}Foo.name === "Foo"(这意味着我们不担心奇怪的边界情况,例如被静态属性覆盖或者类被压缩或混淆等情况),TypeScript 仍然无法轻松地允许 Foo.name 的类型为 "Foo"。这是因为类的静态部分形成了一个类型层次结构,所以如果你有 class Bar extends Foo {⋯},那么 TypeScript 期望 Bar.name 可以赋值给 Foo.name。因此,如果 Foo.name 的类型是 "Foo",那么 Bar.name 也需要是 "Foo" 的类型。但在运行时这几乎不太可能成立。基本上,围绕静态继承存在一大堆问题,TypeScript 大多数情况下试图避免,导致类型没有人们期望的那么强。

有可能在将来,JavaScript 会引入像 C# 中的 nameof 操作符,然后 TypeScript 将支持它(参见 microsoft/TypeScript#1579),然后我们可以以某种官方支持的方式从 TypeScript 类型系统中提取类的名称。

但在这些发生之前,你必须自己指定它。你可以通过手动合并信息来做到这一点,就像这样:

declare namespace MyClass {
  export const name: "MyClass"
}

然后,你可以通过模板文字类型来定义你的键(这是在类型级别获取字符串连接的唯一方法):

const action1 = `${MyClass.name}_action1` as const;
const action2 = `${MyClass.name}_action2` as const;
interface Actions {
  [action1]: string,
  [action2]: number
}

这可能或可能不满足你的需求,但现在要么像这样做一些事情,要么放弃。

Playground 链接到代码

英文:

This isn't possible without workarounds.

The name property of class constructors is of type string, as inherited from the Function interface. The compiler doesn't know the string literal type of the name. See microsoft/TypeScript#43325 and the issues to which it links.

Assuming that we know for sure that at runtime, a class Foo {⋯} will have Foo.name === "Foo" (meaning that we don't worry about weird edge cases where it is overridden by a static property or where the class is minified or mangled, etc), TypeScript still can't easily allow the type of Foo.name to be "Foo". That's because the static side of a class forms a type hierarchy, so if you have class Bar extends Foo {⋯}, then TypeScript expects Bar.name to be assignable to Foo.name. So if Foo.name is of type "Foo" then Bar.name also needs to be of type "Foo". But that's quite unlikely to be true at runtime. Essentially there's a whole mess surrounding static inheritance that TypeScript mostly tries to avoid, leading to somewhat less strong types than people would like.

It's vaguely possible that someday JavaScript will introduce a nameof operator like C# has, and then TypeScript will support that (see microsoft/TypeScript#1579), and then we can extract the name of a class in the TypeScript type system in some officially supported way.

But until and unless any of these happen, you're stuck specifying it yourself. You can do so by manually merging the information in, like this:

declare namespace MyClass {
  export const name: "MyClass"
}

And then you can define your keys via template literal types (that's the only way to get string concatenation at the type level):

const action1 = `${MyClass.name}_action1` as const;
const action2 = `${MyClass.name}_action2` as const;
interface Actions {
  [action1]: string,
  [action2]: number
}

This may or may not meet your needs, but for now, it's either do something like this, or give up.

Playground link to code

huangapple
  • 本文由 发表于 2023年7月20日 21:17:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76730327.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定