打字稿:在 class 内扩展 "this"
Typescript: extending "this" inside class
我有一个 class Foo
,我想用我拥有的一些文字对象动态扩展它。如何在不在单独的界面中手动重复文字对象的键 的情况下保持 this
键入 ?
const someLiteralObject = {
key1: 'foo',
key2: 'bar',
// ...lots of more keys
}
class Foo {
constructor(){
Object.assign(this, someLiteralObject)
}
test() {
console.log(this.key1); //error: Property 'key1' does not exist on type 'Foo'
}
}
const test = new Foo();
console.log(test.key1); //error: Property 'key1' does not exist on type 'Foo'
强类型方式:
class Some { // <= This is representing your Object
key1: string;
key2: string;
}
const someLiteralObject: Some = { // <= As you can see it is of type Some
key1: 'foo',
key2: 'bar',
}
class Foo extends Some { // <= Tell TS we are using all the keys from Some
constructor(){
super(); // <= needed for ts, useless as we did not define a constructor in Some
Object.assign(this, someLiteralObject);
}
test() {
console.log(this.key1); // Yeah!
}
}
const test = new Foo();
console.log(test.key1); // No error!
test.test(); // works fine!
Hacky 方式(例如,当您不知道对象将具有哪些键时)
const someLiteralObject = {
key1: 'foo',
key2: 'bar',
}
class Foo {
constructor(){
Object.assign(this, someLiteralObject)
}
test() {
console.log((this as any).key1); // tell ts to be unsure about the type of this
}
}
const test = new Foo();
console.log((test as any).key1); // tell ts to be unsure about the type of this
编辑:我建议您查看 Mixins in Typescript 并研究那里提供的 Object.assign
和 applyMixins
助手之间的区别。也许那个设计模式才是你真正需要的。
这是一个肮脏但强类型的解决方案,它使用文字对象而不是按照要求重复所有键。内联解释和 TypeScript Playground link.
// Hide these helpers away in your `utils.ts` somewhere, see below what they do.
/**
* Gives Constructor given a instance, like inverse of `InstanceType`.
*
* Second optional parameter is argument tuple for the `constructor()`.
*
* @todo this interface lacks signature for static methods/properties.
*/
export interface IConstructor<T extends object = object, TA extends unknown[] = unknown[]> {
new(...args: TA): T
}
/**
* Overrrides a class to return a instance that includes the given mixin.
*/
export type ClassWithMixin<T extends IConstructor, TMixin extends object> =
IConstructor<InstanceType<T> & TMixin, ConstructorParameters<T>>
// A mixin many keys and/or methods. Use `typeof someLiteralObject` to use it as interface.
const someLiteralObject = {
key1: 'foo',
key2: 'bar',
} as const // <-- `as const` is optional, but handy.
// `this:` type tells method internal scope that `this` is more than TS thinks by default.
class FooPure {
constructor(){
Object.assign(this, someLiteralObject)
}
test(this: this & typeof someLiteralObject) {
console.log(this.key1)
}
}
// And `ClassWithMixin` type tells all other codebase that `Foo` is more than `FooHidden`.
const Foo = FooPure as ClassWithMixin<typeof FooPure, typeof someLiteralObject>
// Works as expected.
const foo = new Foo()
console.log(foo.key1)
foo.test()
class Bar extends Foo {
constructor() {
super()
this.test()
}
}
console.log(new Bar())
我有一个 class Foo
,我想用我拥有的一些文字对象动态扩展它。如何在不在单独的界面中手动重复文字对象的键 的情况下保持 this
键入 ?
const someLiteralObject = {
key1: 'foo',
key2: 'bar',
// ...lots of more keys
}
class Foo {
constructor(){
Object.assign(this, someLiteralObject)
}
test() {
console.log(this.key1); //error: Property 'key1' does not exist on type 'Foo'
}
}
const test = new Foo();
console.log(test.key1); //error: Property 'key1' does not exist on type 'Foo'
强类型方式:
class Some { // <= This is representing your Object
key1: string;
key2: string;
}
const someLiteralObject: Some = { // <= As you can see it is of type Some
key1: 'foo',
key2: 'bar',
}
class Foo extends Some { // <= Tell TS we are using all the keys from Some
constructor(){
super(); // <= needed for ts, useless as we did not define a constructor in Some
Object.assign(this, someLiteralObject);
}
test() {
console.log(this.key1); // Yeah!
}
}
const test = new Foo();
console.log(test.key1); // No error!
test.test(); // works fine!
Hacky 方式(例如,当您不知道对象将具有哪些键时)
const someLiteralObject = {
key1: 'foo',
key2: 'bar',
}
class Foo {
constructor(){
Object.assign(this, someLiteralObject)
}
test() {
console.log((this as any).key1); // tell ts to be unsure about the type of this
}
}
const test = new Foo();
console.log((test as any).key1); // tell ts to be unsure about the type of this
编辑:我建议您查看 Mixins in Typescript 并研究那里提供的 Object.assign
和 applyMixins
助手之间的区别。也许那个设计模式才是你真正需要的。
这是一个肮脏但强类型的解决方案,它使用文字对象而不是按照要求重复所有键。内联解释和 TypeScript Playground link.
// Hide these helpers away in your `utils.ts` somewhere, see below what they do.
/**
* Gives Constructor given a instance, like inverse of `InstanceType`.
*
* Second optional parameter is argument tuple for the `constructor()`.
*
* @todo this interface lacks signature for static methods/properties.
*/
export interface IConstructor<T extends object = object, TA extends unknown[] = unknown[]> {
new(...args: TA): T
}
/**
* Overrrides a class to return a instance that includes the given mixin.
*/
export type ClassWithMixin<T extends IConstructor, TMixin extends object> =
IConstructor<InstanceType<T> & TMixin, ConstructorParameters<T>>
// A mixin many keys and/or methods. Use `typeof someLiteralObject` to use it as interface.
const someLiteralObject = {
key1: 'foo',
key2: 'bar',
} as const // <-- `as const` is optional, but handy.
// `this:` type tells method internal scope that `this` is more than TS thinks by default.
class FooPure {
constructor(){
Object.assign(this, someLiteralObject)
}
test(this: this & typeof someLiteralObject) {
console.log(this.key1)
}
}
// And `ClassWithMixin` type tells all other codebase that `Foo` is more than `FooHidden`.
const Foo = FooPure as ClassWithMixin<typeof FooPure, typeof someLiteralObject>
// Works as expected.
const foo = new Foo()
console.log(foo.key1)
foo.test()
class Bar extends Foo {
constructor() {
super()
this.test()
}
}
console.log(new Bar())