【问题标题】:Typescript: extending "this" inside class打字稿:在课堂上扩展“this”
【发布时间】:2020-06-27 00:23:50
【问题描述】:

我有一个类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'

【问题讨论】:

    标签: typescript class this extends


    【解决方案1】:

    强类型:

    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
    

    【讨论】:

    • literal 对象的 props 列表很大,有什么办法可以避免 Some 和 someLiteralObject 之间的重复吗?
    【解决方案2】:

    编辑:我建议您查看Mixins in Typescript 并研究那里出现的Object.assignapplyMixins 助手之间的区别。也许这种设计模式才是你真正需要的。

    这是一个肮脏但强类型的解决方案,它使用文字对象而不是按照要求重复所有键。内联解释和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())
    

    【讨论】:

    • 有效!你能解释一下 Constructor 和 ExtendedWith 类型是如何工作的吗?
    • @kundasaba 我为类型添加了解释,并建议了一个混合模式。如果完全回答了您的问题,请考虑接受答案。
    猜你喜欢
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 2019-09-18
    • 2020-11-30
    • 1970-01-01
    • 2021-08-10
    • 1970-01-01
    相关资源
    最近更新 更多