简体   繁体   English

打字稿特征实现:如何让通用知道它已被扩展?

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

Hi maybe the title is not right, but I am already trying to specify the problem I am having with my limited knowledge. 嗨,也许标题是不对的,但我已经尝试用我有限的知识来指出我遇到的问题。 Please help to edit it if it's not right. 如果不对,请帮助编辑它。

I am trying to create some trait function to mirror a class. 我正在尝试创建一些特征函数来镜像一个类。 Much more like PHP trait. 更像是PHP特质。


The reasons I am doing this are 我这样做的原因是

  1. Trying use the same setting without writing it twice. 尝试使用相同的设置而不写两次。

    like the minx func here https://www.typescriptlang.org/docs/handbook/mixins.html 像这里的minx功能https://www.typescriptlang.org/docs/handbook/mixins.html

    implementing class requires you to declare the target Class as undefined or etc first. 实现类要求您首先将目标类声明为undefined或etc。

2 . 2。 Is not parent and child. 不是父母和孩子。 It does not extend. 它没有扩展。


The function works well after compiling, but I just do not know how to resolve the TS error. 编译后该功能运行良好,但我不知道如何解决TS错误。

TS can not resolve that B already has A's method and property. TS无法解析B已经拥有A的方法和属性。

Code as below. 代码如下。

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

I am getting errors like 我收到的错误就像

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

[update] [更新]

Thanks for @T ' help, after long try I found how the way to do this. 感谢@T'的帮助,经过长时间的尝试,我发现了如何做到这一点。

First at all, below the type checker. 首先,在类型检查器下方。 To merge static and prototypes. 合并静态和原型。

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

Secondly here is the way to implement it. 其次,这是实现它的方法。

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 ;

So the trait will work in JS and if ur using TS requires to write more to configure types. 所以特性将在JS中工作,如果你使用TS需要写更多的配置类型。

I'm not going to speak to the implementation, it does not work for me, but the question is on how to get the types to workout. 我不会谈论实现,它对我不起作用,但问题是如何让类型进行锻炼。

Decorators can't change the type of a class, this is by design. 装饰器不能改变类的类型,这是设计的。 You can however use a function to transform the type of the class. 但是,您可以使用函数来转换类的类型。

To merge the class properties we can use some conditional and mapped type magic. 要合并类属性,我们可以使用一些条件和映射类型魔术。 The solution: 解决方案:

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 {

}

A bit of explanation: 一点解释:

We need to take the two class types which will be captured in the TTrait and TTarget type parameters and merge them. 我们需要采用将在TTraitTTarget类型参数中捕获的两个类类型并合并它们。 To merge the types we will need a new constructor type that returns an intersection of the two instance types. 要合并类型,我们需要一个新的构造函数类型,它返回两个实例类型的交集。 We can get the instance types using the predefined conditional type InstanceType<T> . 我们可以使用预定义的条件类型InstanceType<T>来获取实例类型。 We can then intersect them to create the new instance type: InstanceType<TTrait> & InstanceType<TTarget> 然后我们可以将它们相交以创建新的实例类型: InstanceType<TTrait> & InstanceType<TTarget>

The constructor also need to copy the parameters from TTarget . 构造函数还需要从TTarget复制参数。 We can use ConstructorParameters to get the parameters from the target constructor and spread them back into the new constructor using tuples in rest parameters . 我们可以使用ConstructorParameters从目标构造ConstructorParameters中获取参数,并使用rest参数中的元组将它们分散回新的构造函数中

The final touch is to add back the statics of each class, removing the constructor in the original classes. 最后一步是添加每个类的静态,删除原始类中的构造函数。 We can do this using Pick which will pick all named properties but not constructor or function signatures. 我们可以使用Pick执行此操作,它将选择所有命名属性,但不会选择构造函数或函数签名。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何知道电子邮件是否已经在Firebase身份验证JavaScript中注册? - How to know if the email has already been registered in firebase authentication, javascript? 如何知道字体(@font-face)是否已经加载? - How to know if a font (@font-face) has already been loaded? javascript - 知道链接是否已经打开 - javascript - know if a link has already been opened 检查一个Typescript class是否已经被实例化 - Check if a Typescript class has already been instantiated 如何让angular知道自定义窗口事件已被触发并需要更新检查 - How do I let angular know a custom window event has been fired and requires update checking 如何在事件处理程序中知道按键事件是否已经完成工作 - How to know if the key up event has been already done its job or not in the event handler 如何使用 google 登录和 firebase web 9 知道用户是刚刚创建还是已经存在? - How to know if a user has just been created or already existed with google signin and firebase web 9? jQuery:如何知道页面是否已刷新 - jQuery: How to know if a page has been refreshed 如何知道bootstrap插件已经初始化 - How to know that bootstrap plugin has been initialized 如何知道上传的文件是否已更改? - How to know if the uploaded file has been changed?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM