Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.4k views
in Technique[技术] by (71.8m points)

vue.js - Typescript generic class parameters

I am currently programming some internal abstract class for project and I need it to be generic (I want it to be extendable).

I want my class to be called as it would extend the T template like Sample extends T, in order to have all the parameters of T. For example if T is Vue, I would have all Vue parameters such as $el or $options without re-declaring Vue nor including it.

So I've got the following :

export namespace SDK {

  export abstract class Sample<T> {

    private static methods: any = {
      hello: Sample.prototype.hello
    }

    abstract callMe () : void;

    x: number = 0
    y: number = 0
    width: number = 1
    height: number = 1

    hello (): void {
      console.log('Sample hello world')
      this.callMe()
    }
  }
}

But I don't know how to process to include the properties of T into Sample.

I would like it to be like:

export namespace SDK {

  export abstract class Sample<T> {

    private static methods: any = {
      hello: Sample.prototype.hello
    }

    abstract callMe () : void;

    x: number = 0
    y: number = 0
    width: number = 1
    height: number = 1

    // T properties (Vue example)
    $el: HTMLElement
    ...

    hello (): void {
      console.log('Sample hello world')
      this.callMe()
    }
  }
}

I would like my class to be called like:

export default class MyComponent extends SDK.Sample<Vue> {
  name: string = 'my-component';

  callMe () : void {
    console.log('called')
  }

  mounted () : void {
    this.hello()
  }
}

I did not find anything about extending from a templated class that allow to have parameters in it.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You cannot extend a generic parameter in Typescript (and in C# and Java either for that matter). What you can do is use a mixins approach.

From your requirements below is what I came up with. I did not actually test with Vue (I don't have an environment setup with that library) but the methods and properties are inherited from both classes as expected so it should work with Vue (if you leave a comment with any problems I can look into them).

I found two shortcoming:

  1. You loose the compiler warning for not implementing abstract methods
  2. You will not be able to access static methods through the derived class.

The function that does the magic

function extendSample<T, R extends { new(): T & Sample }>(componentCtor: new () => T): R {
    // Create a new derived class from the component class
    class DerivedComponent extends (<new () => any>componentCtor) {
        constructor() {
            // Call thec omponent constructor
            super();
            // Call out sample class constructor
            Sample.apply(this)
        }
    }
    // Copy instance methods to DerivedComponent
    Object.getOwnPropertyNames(Sample.prototype).forEach(name => {
        DerivedComponent.prototype[name] = Sample.prototype[name];
    });
    return <R><any>DerivedComponent;
}

Sample code :

export abstract class Sample {
    abstract callMe(): void;
    x: number = 0
    y: number = 0
    width: number = 1
    height: number = 1

    hello(): void {
        console.log('Sample hello world')
        this.callMe()
    }   
}

export class LibaryComponentBase {
    constructor() {
        this.$el = "HTML "
    }
    $el: string;
    public libraryMethod() {
        console.log("libraryMethod");
    }
    static Test() {

    }
}

export default class MyComponent extends extendSample(LibaryComponentBase) {
    name: string = 'my-component';
    constructor() {
        super();
        console.log(this.$el);
        this.libraryMethod();
        this.hello();
    }

    callMe(): void {
        console.log('called')
    }

    mounted(): void {
        this.hello();
    }
}

let my = new MyComponent();
my.callMe();

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...