带有公开参数的构造函数解构

Constructor Destructuring with exposed parameters

我有一个构造函数,它使用解构来简化需要传递的内容以创建具有适当默认值的对象。

export class PageConfig {
  constructor({
    isSliding = false
  }: {
    isSliding?: boolean;
    getList: (pagingInfo: PagingInfo) => Observable<any>;
  }) {}
}

我想将传递给构造函数的属性公开为 public,最好不要重新声明它们。另外我不想有一个中间人对象。例如说我有一个 class 像这样实例化了我的对象:

class UsersPage {

    config = new pageConfig({ this.getList })    

    getList(pagingInfo: PagingInfo) {
      // do work...
    }
}

我希望配置公开两件事:

config.getList()
config.isSliding

我怎样才能有效地做到这一点?

编辑 我试图通过创建一个基础 class 来做到这一点,consturtor 参数和 class 都继承自该基础,但是,如果我在解构中省略属性,我将无法引用它们在构造函数中。

例如

export class PageConfigArgs {
    isSliding?: boolean;
    getList: (pagingInfo: PagingInfo) => Observable<any>;
}

export class PageConfig extends PageConfigArgs {
  constructor({
    isSliding = false
  }: PageConfigArgs ) {
    super();
    this.isSliding = isSliding;
    //this fails since its not declared in the destructuring
    //However I do not want to declare it since it is required
    this.getList = getList;  
  }
}

TypeScript 中没有简单的方法可以通过编程将属性从传递到构造函数的对象复制到正在构造的对象,并让编译器验证它是否安全。 ,即使你可以这样做,你也必须手动将它们复制到构造的对象中。

与解构效果类似的是 Object.assign(), so you could hope to have the constructor be like constructor(x: X){Object.assign(this, x)}... and this does work at runtime. But the compiler does not recognize 函数,它实际上已经设置了属性,并且会警告您:

class FailedPageConfig implements PageConfigArgs { // error!
  // Class 'FailedPageConfig' incorrectly implements interface 'PageConfigArgs'.
  //  Property 'getList' is missing in type 'FailedPageConfig'
  // but required in type 'PageConfigArgs'.
  constructor(config: PageConfigArgs) {
    Object.assign(this, config);
  }
}

您可以通过对所有 "missing" 属性使用 definite assignment assertion 手动修复该问题,但这是您想避免的声明,对吗?

class OkayPageConfig implements PageConfigArgs { 
  getList!: PageConfigArgs["getList"]; // definite assignment
  constructor(config: PageConfigArgs) {
    Object.assign(this, config);
  }
}

那么,我们还能做什么?

我们可以做的一件事是创建一个函数,该函数生成使用 Object.assign() 的 class 构造函数,并使用 type assertion 告诉编译器不要担心它无法验证安全性:

function ShallowCopyConstructor<T>() {
  return class {
    constructor(x: T) {
      Object.assign(this, x);
    }
  } as new (x: T) => T; // assertion here
}

然后你可以这样使用它:

export class PageConfigPossiblyUndefinedIsSliding extends ShallowCopyConstructor<
  PageConfigArgs
>() {}

declare const pcfgX: PageConfigPossiblyUndefinedIsSliding;
pcfgX.getList; // pagingInfo: PagingInfo) => Observable<any>
pcfgX.isSliding; // boolean | undefined

您可以看到 PageConfigPossiblyUndefinedIsSliding 个实例已知有一个 getList 和一个 isSliding 属性。当然,isSlidingboolean | undefined 类型,您想要一个默认的 false 值,这样它就永远不会是 undefined,对吗?以下是我们的做法:

export class PageConfig extends ShallowCopyConstructor<
  Required<PageConfigArgs>
>() {
  constructor(configArgs: PageConfigArgs) {
    super(Object.assign({ isSliding: false }, configArgs));
  }
}

declare const pcfg: PageConfig;
pcfg.getList; // pagingInfo: PagingInfo) => Observable<any>
pcfg.isSliding; // boolean

这里PageConfig扩展了ShallowCopyConstructor<Required<PageConfigArgs>>(),意味着superclass的构造函数需要同时传入getListisSliding属性(使用Required<T> utility type)。

PageConfig 的构造函数只需要一个 PageConfigArgs,并使用另一个 Object.assign() 调用从中组装一个 Required<PageConfigArgs>

所以现在我们有一个 PageConfig class 其构造函数接受 PageConfigArgs 并构造一个 Required<PageConfigArgs>.


我们终于到了您的 UsersPage class。你不能做 new PageConfig({this.getList})。那不是有效的语法。相反,您可以这样做:

class UsersPage {
  config = new PageConfig(this);

  getList(pagingInfo: PagingInfo) {
    return null!;
  }
}

或这个

class UsersPage {
  config = new PageConfig({getList: this.getList});

  getList(pagingInfo: PagingInfo) {
    return null!;
  }
}

或者,如果您不想输入单词 getList 两次,并且不想从 [=] 复制 every 属性 44=],那么你可以创建一个名为 pick 的辅助函数,它从对象中复制命名属性:

function pick<T, K extends keyof T>(obj: T, ...keys: K[]) {
  const ret = {} as Pick<T, K>;
  for (const k of keys) {
    ret[k] = obj[k];
  }
  return ret;
}

然后使用这个:

class UsersPage {
  config = new PageConfig(pick(this, "getList"));

  getList(pagingInfo: PagingInfo) {
    return null!;
  }
}

好吧,那里有很多东西要打开。希望它能给你一些方向。祝你好运!

Link to code