微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

打字稿:自动为构造函数的所有命名参数创建getter

如何解决打字稿:自动为构造函数的所有命名参数创建getter

为了使构造函数调用更具可读性,我通常更喜欢使用命名参数,如下所示:

class MyClass {
  constructor(private args: {a: number,b: string,c: boolean}) {}
}

// Example constructor call:
const x = new MyClass({a: 1,b: "hello",c: true})

但是,我也希望 abc 是公开可读的,即我希望能够调用 x.a、{{1 }} 和 x.b 而不是使用 x.cx.args.ax.args.b。我可以想出几种不同的方法来做到这一点(见下文),但它们都涉及重复的样板文件,以明确创建属性或 getter。

有没有办法可以自动创建这些 getter,例如通过编写装饰器还是使用泛型?

方法 1:显式声明属性并在构造函数中填充它们

这太冗长了!

x.args.c

方法 2:存储 args 并写入 getter

方法 1 更简洁,但我希望自动创建这些 getter。

class MyClass1 {
  a: number;
  b: string;
  c: boolean;
  
  constructor(private args: {a: number,c: boolean}) { 
    this.a = args.a;
    this.b = args.b; 
    this.c = args.c;
  }
}

解决方法

这可以使用装饰器完成您想要的:

// Decorator setup

interface IndexedObject { [name: string]: any }
type Constructor = new(...args: any[]) => IndexedObject

function namedConstructorArgs <T extends Constructor> (targetConstructor: T): T {
  return class proxyConstructor extends targetConstructor {
    constructor(...args: any[]){
      super(args)
      Object.entries(args[0]).forEach(
        eachNamedArg => this[ eachNamedArg[0] ] = eachNamedArg[1]
      )
    }
  }
}

// In main program

abstract class MyClassLoggerProperties {
  a: string
  b: number
}

@namedConstructorArgs
class MyClassLogger extends MyClassLoggerProperties {

  constructor(_: MyClassLoggerProperties) {
    super()
  }

  log() {
    console.log(this.a,this.b)
  }

}

new MyClassLogger( { a: "i like flake number",b: 99 } )
  .log() // logs: I like flake number,99

注意我必须关闭 strictPropertyInitialization 编译器标志,以便编译器接受未在其自己的构造函数中初始化的类的属性。如果您希望保留编译器标志,您也可以将属性初始化为虚拟值。

抽象类用于确保类声明中的类属性和构造函数中的类属性相同,否则编译器不会检查它们是否一致。您可以在这些地方重复属性声明并省略构造函数中的 super() 调用,但它不会是类型安全的。

,

非常感谢 Simon Jacobs for this answer 和随后的评论线程,这似乎是最简洁的方法:

// type containing the properties (not methods) of a type T
type PropertiesFilter<T> = {
  [K in keyof T as T[K] extends ((...args: any) => any) ? never : K]: T[K]
}

abstract class NamedConstructorArgsBaseClass<T> {

  // Constructor takes an object with all the 
  // properties (not methods) of T
  constructor(args: PropertiesFilter<T>) {
    // Set all of those properties on the object being constructed
    Object.entries(args).forEach(
      eachNamedArg => (this as any)[eachNamedArg[0]] = eachNamedArg[1]
    )
  }

}

// in main program

class MyClassLogger extends NamedConstructorArgsBaseClass<MyClassLogger> {
  a: string;
  b: number;

  log() { 
    console.log(this.a,this.b);
  }
}

new MyClassLogger({a: "hi",b: 99}).log() // logs: hi,99

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。