这是一个构造为使用方法扩充数据对象的类。
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
如果没有,是否有更好的方法用访问其数据属性的方法来包装纯JS对象?我没有附属于此抽象类,在上述情况下使用UserViewModel
很好,但是代码库已经具有许多不同的T
类型,如果可能的话,我希望重用它们。 (例如,使UserViewModel
引用User
而不是复制该类型的大部分定义。)
我知道我也可以与类一起导出接口,例如在Extending `this` in Typescript class by Object.assign中,但是我也希望类中的方法知道this
上可用的值。>
答案 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 assertions和type 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>
现在对我很好。希望能有所帮助;祝你好运!