使用打字稿在构造函数中动态设置类属性
Posted
技术标签:
【中文标题】使用打字稿在构造函数中动态设置类属性【英文标题】:Use typescript to dynamically set class property in constructor 【发布时间】:2021-12-31 05:36:31 【问题描述】:我正在尝试创建一个打字稿类MyClass
,并在构造函数中动态设置实例属性:
const myInstance = new MyClass((
someField: 'foo'
))
myInstance.someField // typescript should show this as type string
如何使用 typescript 创建MyClass
以将someField
显示为myInstance
的字符串属性?
我可以使用泛型吗?有可能吗?
【问题讨论】:
喜欢this,也许吧? TypeScript 不允许您使用class
声明来执行此操作,因为对于 class
和 interface
s,密钥必须是静态已知的。相反,您需要编写使用类型断言或其他声明来告诉编译器MyClass
充当构造函数,生成具有动态键的某些对象类型。如果这对你有用,我可能会写一个答案(尽管我有 85% 的把握我之前写过这个答案,所以我应该会找到它)。如果它不起作用,请edit问题中的代码来演示不满意的用例。
这行得通!为了清楚起见,我编辑了your example。
虽然我很难理解这一行:“as new object
到 Record<string, unknown>
的更改是否提高了清晰度。我想你做出这个改变是为了安抚一些(在我看来被误导的)linter 规则,但这并没有真正让任何事情变得更清楚(特别是如果你尝试为T
使用接口类型并发现它匹配object
但不匹配Record<string, unknown>
)。除非您的问题是关于此类事情的,否则我计划在我发布的答案中留下object
(当我有机会写下来时)。见github.com/microsoft/TypeScript/issues/…
【参考方案1】:
从概念上讲,您想说class MyClass<T extends object> extends T
或class MyClass<T extends object> implements T
,但TypeScript 不允许class
实例或interface
具有动态键。 class
或 interface
声明的所有键必须是静态已知的。因此,如果您想要这种行为,您需要做的不是class
声明。
相反,您可以描述所需的MyClass<T>
实例的类型以及MyClass
类构造函数的类型,然后使用type assertion 告诉编译器您的实际构造函数对象属于该类型.
假设您想要的MyClass<T>
类实例具有T
的所有属性,外加一个名为someMethodIGuess()
的方法。那么你的MyClass<T>
类型可以这样定义:
type MyClass<T extends object> = T &
someMethodIGuess(): void;
您希望您的MyClass
类构造函数有一个construct signature,它为某些generic T
对象类型接受T
类型的参数,并生成MyClass<T>
的实例。可以这样定义:
type MyClassConstructor =
new <T extends object>(arg: T): MyClass<T>
要实现这个类,我们可以使用 [Object.assign()
] 将构造函数参数属性以及我们需要的任何方法或其他东西复制到实例中。
const MyClass = class MyClass
constructor(arg: any)
Object.assign(this, arg);
someMethodIGuess()
但是当然,如果我们这样离开它,编译器将不会将MyClass
视为MyClassConstructor
(毕竟它不能有动态键,我什至没有尝试告诉它@ 987654348@ 这里)。我们需要一个类型断言来告诉它,像这样:
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
声明创建 MyClass
的通用子类,您将收到错误:
class SubClass<T extends object> extends MyClass<T> // error
anotherMethod()
如果你需要这种东西,你会发现自己必须使用与以前相同的技巧来处理子类:
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;
Playground link to code
【讨论】:
【参考方案2】:这个怎么样?
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());
TS playground
【讨论】:
谢谢,但我想用“new”运算符来呼叫课程。所以这对我不起作用。但再次感谢您!以上是关于使用打字稿在构造函数中动态设置类属性的主要内容,如果未能解决你的问题,请参考以下文章