Машинопись: расширение "этого" внутри класса - PullRequest
0 голосов
/ 15 марта 2020

У меня есть класс 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'

Ответы [ 2 ]

2 голосов
/ 16 марта 2020

РЕДАКТИРОВАТЬ: Я предлагаю вам взглянуть на Mixins в Typescript и исследовать разницу между Object.assign и applyMixins хелпер, представленный там. Возможно, вам нужен именно этот шаблон проектирования.

Вот грязное, но строго типизированное решение, использующее буквальный объект и не повторяющее все его ключи в соответствии с запросом. Встроенные пояснения и ссылка на TypeScript Playground .

// 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())
0 голосов
/ 15 марта 2020

Строго набранный способ:

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!

Хакерский способ (например, когда вы не знаете, какие ключи будут у объекта)

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
...