我想从一个类扩展但删除一些属性

问题描述 投票:0回答:5

我有一些用于 CRUD API 服务类的类 我想扩展它,但只保留了一些属性 为了考试,这是我的课

class Parent {
  public propertyToKeep: any;
  public propertyToDelete: any;
  constructor() { }
}

这是儿童班

class Child extends Parent {
  constructor() {
    super();
  }
}

另一个文件 我不想看到和访问的地方

export class comeComponent {
  constructor(private child: Child) {
    this.child.propertyToKeep // work
    this.child.propertyToDelete // error and I can't even see it
  }
}
javascript angular typescript class oop
5个回答
32
投票

我刚刚遇到了与您相同的用例,这就是我的做法:

const Omit = <T, K extends keyof T>(Class: new () => T, keys: K[]): new () => Omit<T, typeof keys[number]> => Class;

然后你就可以像这样使用它了:

class Child extends Omit(Parent, ['propertyToDelete']) {}

如您所见,child 现在只有一个属性(它也适用于方法)。

如果您正在处理 NestJS API,来自 @nestjs/swagger 的包有一些不错的助手。他们的实现更加复杂,所以我猜他们保留了其他东西,比如他们自己的属性装饰器(我对 Typescript 还很陌生,所以也许我错过了他们所做的一切的重点)。

P.S: 法国小伙第一次尝试用不完美的英语回答所以请友善^^


7
投票

这是一种方法:

class Parent {
  propertyToKeep = 'hi';
  propertyToDelete = 'bye';
  constructor() {}
}

class Child extends Parent {
  constructor() {
    super();
    delete this.propertyToDelete;
  }
}

const myObject = new Child();

console.log(myObject);
/* OUTPUT:
  {
    "propertyToKeep": "hi"
  }
*/


3
投票

那是不可能的。如果你在父类中声明一个属性,你就不能限制他在子类中的可见性。

从模型设计的角度来看,这也没有意义。你在这里暴露的问题表明你的类层次结构设计得不好,你必须重新思考和重新设计它。


2
投票

您需要使用

Object.defineProperty
函数,对描述符进行限制,enumerablefalse
getter
setter
有特定条件,这里有一个完整的例子:

//A small example of how to make an invisible property in Child class.

class Parent{
 
 constructor(){
 
  this.propertyToKeep = "Visible";
  this.propertyToDelete = "Not visible in subclass child";
 
 }
}

Object.defineProperty(Parent.prototype, "propertyToDelete",  {enumerable: false,
 configurable: true,
 get: function(){
     if(!(this instanceof Child)){

        return this._propertyToDelete;
     }
 },
 set: function(v){
    
    if(!(this instanceof Child)){

        this._propertyToDelete = v;
    }
    

 }});

 Object.freeze(Parent.prototype);

class Child extends Parent {
  constructor() {
    super();
  }
}

//console.log(Child.prototype);

let chd = new Child();

console.log("Child: --------------------------------");

console.log(chd);

console.log(chd.propertyToDelete); //Undefined

console.log("Parent: -------------------------------");

let prt = new Parent();

console.log(prt);

console.log(prt.propertyToDelete); //"Not visible in subclass child"

/*let chdObj = Object.getOwnPropertyDescriptors(Child.prototype);

console.log(chdObj);*/


class SomeComponent{

  #child;

  constructor(child) {
    
    this.#child = child;

    

    console.log(this.#child); //{propertyToKeep: "Visible"}

    console.log(this.#child.propertyToKeep /*work*/);
    
    console.log(this.#child.propertyToDelete /*undefined*/);


  }
}


//Now i will invoke SomeComponent

console.log("SomeComponent: -------------------------");

let sc = new SomeComponent(new Child());


0
投票

所以我是这样做的,实际上我的答案是JS和TS的结合。从类型安全的角度来看,我真的很喜欢这个答案。但仍然可以在运行时访问该道具,这对我的情况来说是一个交易破坏者(我使用第 3 方库,它自动为我生成一些代码,我需要对其进行一些修改)。

然后我尝试将它与这个答案混合并创建我自己的辅助函数:

exclude.util.ts

export function Exclude<T, K extends keyof T>(
  Parent: new () => T,
  keys: K[],
): new () => Omit<T, (typeof keys)[number]> {
  class Child extends (Parent as any) {}

  for (const key of keys) {
    Object.defineProperty(Child.prototype, key, {
      enumerable: false,
      configurable: true,
      get: function () {
        if (!(this instanceof Child)) {
          return this._propertyToDelete;
        }
      },
      set: function (v) {
        if (!(this instanceof Child)) {
          this._propertyToDelete = v;
        }
      },
    });
  }

  Object.freeze(Child.prototype);

  return Child as any;
}

exclude.util.spec.ts

这是用 Jest 编写的这个辅助函数的单元测试。

import { Exclude } from './exclude.util';

describe('Exclude', () => {
  it('should remove "propertyToDelete" from the child class', () => {
    class Parent {
      constructor(
        public propertyToKeep = 'Will be inherited',
        public propertyToDelete = 'Will return undefined',
      ) {}
    }
    class Child extends Exclude(Parent, ['propertyToDelete']) {}

    expect(new Child().propertyToKeep).toBe('Will be inherited');
    // @ts-ignore
    expect(new Child()['propertyToDelete']).toBeUndefined();
  });

  it('should remove several properties from the child class', () => {
    class Parent {
      constructor(
        public propertyToKeep = 'Will be inherited',
        public deletedProp1 = 'Will return undefined',
        public deletedProp2 = 'Will return undefined',
      ) {}
    }
    class Child extends Exclude(Parent, [
      'deletedProp1',
      'deletedProp2',
    ]) {}

    expect(new Child().propertyToKeep).toBe('Will be inherited');
    // @ts-ignore
    expect(new Child()['deletedProp1']).toBeUndefined();
    // @ts-ignore
    expect(new Child()['deletedProp2']).toBeUndefined();
  });
});
© www.soinside.com 2019 - 2024. All rights reserved.