具有构造函数参数的Object.assign()属性的TypeScript数据对象类

时间:2019-07-19 23:37:56

标签: typescript

这是一个构造为使用方法扩充数据对象的类。

export default class ViewModel<T> {
  constructor(props: T) {
    Object.assign(this, props);
  }

  foo() {
    console.log('This class and children have methods as well as data');
  }
}

TypeScript是否可能了解T的所有道具都存在于ViewModel<T>的实例上?

type User = { id: number; name: string };

const m = new ViewModel<User>({ id: 1, name: 'Alice' });

m.foo(); // fine

console.log(m.id); // Property 'id' does not exist

Playground

如果没有,是否有更好的方法用访问其数据属性的方法来包装纯JS对象?我没有附属于此抽象类,在上述情况下使用UserViewModel很好,但是代码库已经具有许多不同的T类型,如果可能的话,我希望重用它们。 (例如,使UserViewModel引用User而不是复制该类型的大部分定义。)

我知道我也可以与类一起导出接口,例如在Extending `this` in Typescript class by Object.assign中,但是我也希望类中的方法知道this上可用的值。

1 个答案:

答案 0 :(得分:1)

如果要扩展或实现的类型没有静态已知的成员,TypeScript确实不希望您使用class。像T这样的通用类型不起作用:

class Foo<T> implements T {} // error! 
// A class can only implement an object type or intersection of 
// object types with statically known members.

Typescript可以在类型系统中表示这样的类构造函数:

type BarConstructor = new <T>(props: T) => T & { foo(): void }; // okay

,如果您有该类型的对象,则可以按照您想要使用ViewModel的方式使用它:

function makeABarAndDoThings(Bar: BarConstructor) {
  const bar = new Bar({ a: 1, b: "2", c: true });
  bar.foo(); // okay
  bar.a; // okay
  bar.b; // okay
  bar.c; // okay
}

但是您不能使用class来构建编译器理解为这种通用类型的构造函数。


请注意,在编写class Foo {}时,您同时引入了一个名为Foo的名为 type (与该类的实例相对应)和一个名为 value的值。 称为Foo,该类的构造函数。

由于我们不能直接使用您的ViewModel类实现,因此我们将对其重命名,然后使用type assertionstype aliases创建一个类型和一个名为ViewModel的行为确实符合您的期望。

这是重命名的类:

class _ViewModel<T> {
  constructor(props: T) {
    Object.assign(this, props);
  }
  foo() {
    console.log("This class and children have methods as well as data");
  }
}

这是新的类型和值组合:

type ViewModel<T> = _ViewModel<T> & T;
const ViewModel = _ViewModel as new <T>(props: T) => ViewModel<T>;

类型ViewModel<T>定义为_ViewModel<T>本身的T的{​​{3}}。新的ViewModel值在运行时与_ViewModel相同,但是在编译时我们断言它是一个通用构造函数,它接受类型为T的参数并产生一个{ {1}}。


让我们看看它是如何工作的:

ViewModel<T>

现在对我很好。希望能有所帮助;祝你好运!

intersection