繁体   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