打字稿泛型类参数

Posted

技术标签:

【中文标题】打字稿泛型类参数【英文标题】:Typescript generic class parameters 【发布时间】:2018-05-02 15:37:24 【问题描述】:

我目前正在为项目编写一些内部抽象类,我需要它是通用的(我希望它是可扩展的)。

我希望调用我的类,因为它会扩展像 Sample extends T 这样的 T 模板,以便拥有 T 的所有参数。 例如,如果 T 是 Vue,我将拥有所有 Vue 参数,例如 $el$options,而无需重新声明 Vue 也不包含它。

所以我有以下内容:

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()
    
  

但我不知道如何处理将 T 的属性包含到 Sample 中。

我希望它是这样的:

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()
    
  

我希望我的班级被称为:

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

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

  mounted () : void 
    this.hello()
  

我没有找到任何关于从允许在其中包含参数的模板类扩展的信息。

【问题讨论】:

澄清一下:你想扩展 Sample 还是 Sample 扩展 T?他们是不同的 我想要 Sample 扩展 T. 【参考方案1】:

您不能在 Typescript 中扩展泛型参数(在 C# 和 Java 中也一样)。您可以做的是使用mixins 方法。

根据您的以下要求是我想出的。我实际上并没有使用 Vue 进行测试(我没有使用该库设置环境),但是方法和属性是按预期从两个类继承的,因此它应该可以与 Vue 一起使用(如果您对任何问题发表评论,我可以看看进入他们)。

我发现了两个缺点:

    您因未实现抽象方法而失去编译器警告 您将无法通过派生类访问静态方法。

施展魔法的函数

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;

示例代码:

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();

【讨论】:

不确定为什么要将方法从构造函数原型复制到 mixin 原型。 mixin extends 构造函数不会自动实现这一点吗? @jcalz 构造函数将是库基类(例如 vue),mixin 是示例类。所以DerivedComponent通过继承从库基类中获取方法,Sample通过复制获取方法 哦,对不起,我现在明白了。 感谢您的回答。 @jcalz 方法比我现在预期的要多,但你的观点将来可能对我有用。【参考方案2】:

我认为@TitianCernicovaDragomir 基本上可以使用 mixins。为了完整起见,我将发布类似的代码,因为我认为我们采用的方法略有不同,它们的优缺点也有所不同。

以下代码确实强制您实现abstract 方法并允许您访问静态成员。但是您为此付出了代价,因为您使用了私有的未导出名称,这最终会阻止您将此库设为供他人使用的库。我认为有一些解决方法,但我不想在这个兔子洞里走得太远。

不管怎样,这里是Sample

export namespace SDK 

  export type Constructor<T> = 
    new(...args: any[]): T;
    readonly prototype: T;
  

  export function Sample<C extends Constructor<>>(ctor: C) 
    abstract class Sample extends ctor 
      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()
      

    
    return Sample;
  


及其用法:

export default class MyComponent extends SDK.Sample(Vue) 
  name: string = 'my-component';

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

  mounted () : void 
    this.hello()
  

祝你好运!

【讨论】:

绝对是我需要的。感谢您的解释!

以上是关于打字稿泛型类参数的主要内容,如果未能解决你的问题,请参考以下文章

打字稿可变泛型类,接受扩展公共基础的可变数量的类对象?

如何在打字稿的泛型类中创建类型为“T”的新对象?

打字稿泛型:从函数参数的类型推断类型?

Kotlin泛型 ① ( 泛型类 | 泛型参数 | 泛型函数 | 多泛型参数 | 泛型类型约束 )

17.scala的泛型类

java泛型——泛型类泛型方法泛型接口