簡體   English   中英

打字稿特征實現:如何讓通用知道它已被擴展?

[英]Typescript Trait Implementation: how to let generic know it has been already been extended?

嗨,也許標題是不對的,但我已經嘗試用我有限的知識來指出我遇到的問題。 如果不對,請幫助編輯它。

我正在嘗試創建一些特征函數來鏡像一個類。 更像是PHP特質。


我這樣做的原因是

  1. 嘗試使用相同的設置而不寫兩次。

    像這里的minx功能https://www.typescriptlang.org/docs/handbook/mixins.html

    實現類要求您首先將目標類聲明為undefined或etc。

2。 不是父母和孩子。 它沒有擴展。


編譯后該功能運行良好,但我不知道如何解決TS錯誤。

TS無法解析B已經擁有A的方法和屬性。

代碼如下。

export const trait = ( Orig : any ) : ClassDecorator =>
  < T extends Function >( Tgt : T ) : void => {

    let _stats =
        Object
        .getOwnPropertyNames( Orig )
        .filter( prop =>
            prop != 'length'
            && prop != 'prototype'
            && prop != 'name'
        )
    ;

    for( let _stat of _stats )
      Object.defineProperty( 
        Tgt
        , _stat
        , Object.getOwnPropertyDescriptor( Orig , _stat ) || {}
      )


    let _insts =
        Object
        .getOwnPropertyNames( Orig.prototype )
        .filter( prop =>
            prop != 'constructor'
        ) 
    ;

    for( let _inst of _insts )
        Object.defineProperty( 
          Tgt.prototype
          , _inst
          , Object.getOwnPropertyDescriptor( Orig.prototype , _inst ) || {}
        )

  } ;



class A {

    propA = 1 ;

    static propB = 2 ;

} ;

trait( A ) 
class B {

    PropC = 3 ;

    static propD = 4 ;

} ;


console.log( B.propA ) ;

console.log( new B().PropB ) ;

我收到的錯誤就像

Property 'propA' does not exist on type 'typeof B'. Did you mean 'propD'?

[更新]

感謝@T'的幫助,經過長時間的嘗試,我發現了如何做到這一點。

首先,在類型檢查器下方。 合並靜態和原型。

type mir< C , O > = 
    { 
        new( ... agrs : any ) : 
            Inst< C >
            & { [ I in keyof InstanceType< O > ] ?: InstanceType< O >[ I ] ; }
    }
    & C
    & { [ I in keyof O ] ?: O[ I ] ; }
;

其次,這是實現它的方法。

class orig {}

@trait( orig )
class tgt {}
// js will work here but ts error will ocurr.
// therefore extend the types as well.

const Tgt : mir< tgt , orig >  = tgt ;

所以特性將在JS中工作,如果你使用TS需要寫更多的配置類型。

我不會談論實現,它對我不起作用,但問題是如何讓類型進行鍛煉。

裝飾器不能改變類的類型,這是設計的。 但是,您可以使用函數來轉換類的類型。

要合並類屬性,我們可以使用一些條件和映射類型魔術。 解決方案:

type Constructor = new (...a: any[]) => any;
type Merge<TTrait extends Constructor, TTarget extends Constructor> =
    (new(...a: ConstructorParameters<TTarget>) => InstanceType<TTrait> & InstanceType<TTarget>) & Pick<TTarget, keyof TTarget> & Pick<TTrait, keyof TTrait> 

const trait = <TTrait extends Constructor>(Orig: TTrait) => 
    <TTarget extends Constructor>(Tgt: TTarget) : Merge<TTrait, TTarget> => {
        // perform patching 
        return Tgt as any; // assertion required
}



class A {
    propA = 1;
    static propB = 2;
};

const B = trait(A)(class {
    PropC = 3;
    static propD = 4;
});


console.log(B.propB);
console.log(B.propD);

console.log(new B().PropC);
console.log(new B().propA);


class C extends B {

}

一點解釋:

我們需要采用將在TTraitTTarget類型參數中捕獲的兩個類類型並合並它們。 要合並類型,我們需要一個新的構造函數類型,它返回兩個實例類型的交集。 我們可以使用預定義的條件類型InstanceType<T>來獲取實例類型。 然后我們可以將它們相交以創建新的實例類型: InstanceType<TTrait> & InstanceType<TTarget>

構造函數還需要從TTarget復制參數。 我們可以使用ConstructorParameters從目標構造ConstructorParameters中獲取參數,並使用rest參數中的元組將它們分散回新的構造函數中

最后一步是添加每個類的靜態,刪除原始類中的構造函數。 我們可以使用Pick執行此操作,它將選擇所有命名屬性,但不會選擇構造函數或函數簽名。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM