使用打字稿在构造函数中动态设置class 属性
Use typescript to dynamically set class property in constructor
我正在尝试创建一个打字稿 class MyClass
并在构造函数中动态设置实例属性:
const myInstance = new MyClass(({
someField: 'foo'
}))
myInstance.someField // typescript should show this as type string
如何使用打字稿创建 MyClass
以将 someField
显示为 myInstance
的字符串 属性?
我可以使用泛型吗?有可能吗?
这个怎么样?
class MyClass{
constructor(props: any) {
Object.assign(this, props);
}
baz(): string {
return "Hello World";
}
static create<T>(props = {} as T) {
return new MyClass(props || {}) as MyClass & T
}
}
const o = MyClass.create({
foo: 1,
bar: "Hello World"
});
console.log(o.foo);
console.log(o.bar);
console.log(o.baz());
从概念上讲,您想说 class MyClass<T extends object> extends T
或 class MyClass<T extends object> implements T
之类的内容,但 TypeScript 不允许 class
实例或 interface
具有动态键。 class
或 interface
声明的所有键必须是静态已知的。因此,如果您想要这种行为,您需要做一些 class
声明以外的事情。
相反,您可以描述所需 MyClass<T>
实例的类型以及 MyClass
class 构造函数的类型,然后使用 type assertion 来告诉编译器您的实际构造函数对象属于该类型。
假设您想要的 MyClass<T>
class 实例具有 T
的所有属性,外加一个名为 someMethodIGuess()
的方法。那么你的 MyClass<T>
类型可以这样定义:
type MyClass<T extends object> = T & {
someMethodIGuess(): void;
}
您希望 MyClass
class 构造函数具有 construct signature that takes an argument of type T
for some generic T
对象类型,并生成 MyClass<T>
的实例。可以这样定义:
type MyClassConstructor = {
new <T extends object>(arg: T): MyClass<T>
}
要实现这个 class 我们可以使用 [Object.assign()
] 将构造函数参数属性复制到实例中,加上我们需要的任何方法或其他东西。
const MyClass = class MyClass {
constructor(arg: any) {
Object.assign(this, arg);
}
someMethodIGuess() {
}
}
当然,如果我们这样保留它,编译器不会将 MyClass
视为 MyClassConstructor
(毕竟它不能有动态键,我什至没有试着在这里告诉它 T
)。我们需要一个类型断言来告诉它,像这样:
const MyClass = class MyClass {
constructor(arg: any) {
Object.assign(this, arg);
}
someMethodIGuess() {
}
} as MyClassConstructor;
编译没有错误。再次注意,编译器无法理解 MyClass
实现符合 MyClassConstructor
类型。通过使用类型断言,我将确保正确实现它的负担从编译器(它不能这样做)转移到我(或者你,如果你使用这段代码)。所以我们应该非常小心地检查我们做了什么并且我们没有写错东西(例如,Object.assign(arg, this);
而不是 Object.assign(this, arg);
将是一个问题)。
我们来测试一下:
const myInstance = new MyClass(({
someField: 'foo'
}))
console.log(myInstance.someField.toUpperCase()); // FOO
myInstance.someMethodIGuess(); // okay
看起来不错!编译器期望 myInstance
具有 string
类型的 someField
属性 以及 someMethodIGuess()
方法。
就是这样。请注意,使用 MyClass
的方式与使用另一个 class 构造函数的方式相同。例如,编译器永远不会对具有动态键的 class
声明感到满意,因此如果您尝试使用 class
声明创建 MyClass
的通用子 class ,你会得到一个错误:
class SubClass<T extends object> extends MyClass<T> { // error
anotherMethod() { }
}
如果你需要那种东西,你会发现自己不得不使用与以前相同的技巧来处理 subclass:
type SubClass<T extends object> = MyClass<T> & { anotherMethod(): void };
type SubClassConstructor = { new <T extends object>(arg: T): SubClass<T> };
const SubClass = class SubClass extends MyClass<any> {
anotherMethod() { }
} as SubClassConstructor;
我正在尝试创建一个打字稿 class MyClass
并在构造函数中动态设置实例属性:
const myInstance = new MyClass(({
someField: 'foo'
}))
myInstance.someField // typescript should show this as type string
如何使用打字稿创建 MyClass
以将 someField
显示为 myInstance
的字符串 属性?
我可以使用泛型吗?有可能吗?
这个怎么样?
class MyClass{
constructor(props: any) {
Object.assign(this, props);
}
baz(): string {
return "Hello World";
}
static create<T>(props = {} as T) {
return new MyClass(props || {}) as MyClass & T
}
}
const o = MyClass.create({
foo: 1,
bar: "Hello World"
});
console.log(o.foo);
console.log(o.bar);
console.log(o.baz());
从概念上讲,您想说 class MyClass<T extends object> extends T
或 class MyClass<T extends object> implements T
之类的内容,但 TypeScript 不允许 class
实例或 interface
具有动态键。 class
或 interface
声明的所有键必须是静态已知的。因此,如果您想要这种行为,您需要做一些 class
声明以外的事情。
相反,您可以描述所需 MyClass<T>
实例的类型以及 MyClass
class 构造函数的类型,然后使用 type assertion 来告诉编译器您的实际构造函数对象属于该类型。
假设您想要的 MyClass<T>
class 实例具有 T
的所有属性,外加一个名为 someMethodIGuess()
的方法。那么你的 MyClass<T>
类型可以这样定义:
type MyClass<T extends object> = T & {
someMethodIGuess(): void;
}
您希望 MyClass
class 构造函数具有 construct signature that takes an argument of type T
for some generic T
对象类型,并生成 MyClass<T>
的实例。可以这样定义:
type MyClassConstructor = {
new <T extends object>(arg: T): MyClass<T>
}
要实现这个 class 我们可以使用 [Object.assign()
] 将构造函数参数属性复制到实例中,加上我们需要的任何方法或其他东西。
const MyClass = class MyClass {
constructor(arg: any) {
Object.assign(this, arg);
}
someMethodIGuess() {
}
}
当然,如果我们这样保留它,编译器不会将 MyClass
视为 MyClassConstructor
(毕竟它不能有动态键,我什至没有试着在这里告诉它 T
)。我们需要一个类型断言来告诉它,像这样:
const MyClass = class MyClass {
constructor(arg: any) {
Object.assign(this, arg);
}
someMethodIGuess() {
}
} as MyClassConstructor;
编译没有错误。再次注意,编译器无法理解 MyClass
实现符合 MyClassConstructor
类型。通过使用类型断言,我将确保正确实现它的负担从编译器(它不能这样做)转移到我(或者你,如果你使用这段代码)。所以我们应该非常小心地检查我们做了什么并且我们没有写错东西(例如,Object.assign(arg, this);
而不是 Object.assign(this, arg);
将是一个问题)。
我们来测试一下:
const myInstance = new MyClass(({
someField: 'foo'
}))
console.log(myInstance.someField.toUpperCase()); // FOO
myInstance.someMethodIGuess(); // okay
看起来不错!编译器期望 myInstance
具有 string
类型的 someField
属性 以及 someMethodIGuess()
方法。
就是这样。请注意,使用 MyClass
的方式与使用另一个 class 构造函数的方式相同。例如,编译器永远不会对具有动态键的 class
声明感到满意,因此如果您尝试使用 class
声明创建 MyClass
的通用子 class ,你会得到一个错误:
class SubClass<T extends object> extends MyClass<T> { // error
anotherMethod() { }
}
如果你需要那种东西,你会发现自己不得不使用与以前相同的技巧来处理 subclass:
type SubClass<T extends object> = MyClass<T> & { anotherMethod(): void };
type SubClassConstructor = { new <T extends object>(arg: T): SubClass<T> };
const SubClass = class SubClass extends MyClass<any> {
anotherMethod() { }
} as SubClassConstructor;