【问题标题】:TypeScript avoid repeated class properties when using default valuesTypeScript 在使用默认值时避免重复的类属性
【发布时间】:2020-05-11 09:59:07
【问题描述】:

假设我有这个界面:

// foo.d.ts
export default interface IFoo {
  bar: number;
  faz: string;
  moo: {
    cat: boolean;
    cow: boolean;
  };
}

我基于此创建了一个类(模型):

// fooModel.ts
import IFoo from "./interfaces/foo";

export default class FooModel implements IFoo {
  // now I don't like this
  bar = 0;
  faz = "";
  moo = {
    cat: false,
    cow: true
  }

  // optional property because default values
  // I also don't like this because I have to type the prop name like 3 times
  constructor(_foo?: IFoo) {
    if(_foo === undefined) return;
    this.bar = _foo.bar;
    this.faz = _foo.faz;
    this.moo = {
      cat: _foo.moo.cat,
      cow: _foo.moo.cow
    }
  }
}

我这样称呼它

// fooController.ts
import FooModel from "./fooModel";
import IFoo from "../interfaces/foo";

export default class FooController {
  getFoo(params?: object) {
    return new FooModel(params)
  }
}

为了获得具有默认值的东西,这需要做很多工作。如您所见,我必须输入catcow 属性4 次才能正确初始化。当你有一个大模型时,这会导致很多混乱。

有没有更好的方法来做到这一点?

【问题讨论】:

  • FooModel中的属性不需要输入,它们的类型已经在IFoo中定义:typescriptlang.org/play?#code/…
  • @jonrsharpe 是的,确实,但我希望它们具有默认值.. 这在接口中是不可能的
  • 那我就更不清楚你的问题是什么了。
  • @jonrsharpe 我为您更新了问题,并实施了您的评论
  • “我不喜欢这个” - 强硬?您不能将默认值放在接口中,因为它们在运行时不存在。您可以使用typescriptlang.org/play?#code/… 之类的名称,或者直接缩短为this.moo = _foo.moo。或者,如果您想改用模块,改用模块

标签: typescript oop


【解决方案1】:

这个怎么样?

type PropertiesOnly<T> = {
  [K in keyof T]: T[K] extends Function ? never : T[K]
}

export type IFoo = PropertiesOnly<FooModel>

// For your particular example, it's also safe to just do following:
// export type IFoo = FooModel

export default class FooModel {
  bar = 0;
  faz = "";
  moo = {
    cat: false,
    cow: true
  }

  constructor(_foo?: IFoo) {
    if (_foo === undefined) return this;
    Object.assign(this, _foo);
    this.moo = Object.assign({}, _foo.moo);
  }
}

在TS中声明一个类同时会为你创建一个同名的类型,也就是它的实例的接口。因此,在您的示例中,FooModel 作为一种类型与您手动声明的IFoo 接口几乎相同。

作为预防措施,我还创建了一个实用程序类型PropertiesOnly,以帮助剥离FooModel 中存在的任何潜在方法。

另一个优化是使用Object.assign 取消冗长代码,因此您不必重复输入这些属性。

【讨论】:

  • 这可能有助于更大的对象(我有),谢谢!
  • 从具体类派生类型使得拥有IFoo 毫无意义。由于它只有 props,消费者无法通过 IFoo 访问 FooModel 可能拥有的任何方法。它还破坏了接口隔离(实现多个不同接口的类,因此消费者只知道他们需要的属性和方法)。
  • @jonrsharpe 所以你会建议这不是解决问题的好方法吗?
  • @jonrsharpe 作为一个实用主义者,我对它的好坏没有真正的看法。毕竟这是一个解决方案,所以它可能在某个地方占有一席之地。由于 IFoo 没有方法,你看我故意删除它们,我认为 OP 想要那个。
  • 是的,我理解面向接口的编码方式的好处。但我不确定 OP 是否真的想要它,或者他可能根本不知道可以从 FooModel 派生出 IFoo 一次性使用。
猜你喜欢
  • 1970-01-01
  • 2020-08-03
  • 2021-06-30
  • 1970-01-01
  • 1970-01-01
  • 2020-09-09
  • 1970-01-01
  • 1970-01-01
  • 2016-09-06
相关资源
最近更新 更多