我有一些用于 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
}
}
我刚刚遇到了与您相同的用例,这就是我的做法:
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: 法国小伙第一次尝试用不完美的英语回答所以请友善^^
这是一种方法:
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"
}
*/
那是不可能的。如果你在父类中声明一个属性,你就不能限制他在子类中的可见性。
从模型设计的角度来看,这也没有意义。你在这里暴露的问题表明你的类层次结构设计得不好,你必须重新思考和重新设计它。
您需要使用
Object.defineProperty
函数,对描述符进行限制,enumerable 为 false 和 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());
所以我是这样做的,实际上我的答案是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();
});
});