类型允许字段名称,自身及其派生类的属性

时间:2018-11-28 16:05:08

标签: typescript

假设我们有一个基类,该基类应该声明一个方法,该方法接受类属性之一的名称或任何派生类的属性名称作为字符串参数:

export abstract class BaseClass {
    public someField1: number = 2;

    public get someProperty1(): number {
        return 1;
    }

    public someMethod1(): void {
    }

    // TODO: What is the proper type for propertyName?
    protected method(propertyName: string): void {
        const propertyValue: any = this[propertyName];
        // ...
    }
}

我们有一些派生类经过method()的测试:

export class DerivedClass extends BaseClass {
    protected someField2: number = 2;

    protected get someProperty2(): number {
        return 1;
    }

    protected someMethod2(): void {
    }

    public test(): void {
        super.method("someField1"); // Allowed
        super.method("someProperty1"); // Allowed
        super.method("someMethod1"); // Not allowed

        super.method("someField2"); // Allowed
        super.method("someProperty2"); // Allowed
        super.method("someMethod2"); // Not allowed

        super.method(""); // Not allowed
        super.method("qwerty"); // Not allowed
    }
}

这里是Playground

在上面的示例中,我使用string作为method()参数的类型。但是在这种情况下,任何字符串都可以传入,并且编译器无法验证它是否是现有属性的名称。

此外,最好从允许值列表中排除方法名称("someMethod1""someMethod2")。

propertyName应该只允许使用哪种类型的字段BaseClass和任何派生类的名称?

1 个答案:

答案 0 :(得分:1)

您几乎可以实现您想做的事情。您可以使用多态this类型来引用当前类(这样它将代表redrived类中的派生类和基类中的基类)。我们还可以使用keyof获取一种类型的键(在这种情况下为this类型)。

有两个限制:

  • keyof不返回类型的私有属性,这仅适用于公共属性
  • keyof将返回所有属性。尽管可以过滤掉函数,但这在类内部不起作用(因为this本质上是一个自由类型参数,因此众所周知,因为它可以是此类或派生类,因此我们需要条件类型进行过滤,除非这些类型是完全已知的,否则无法解决。

代码:

export abstract class BaseClass {
    public someField1: number = 2;

    public get someProperty1(): number {
        return 1;
    }

    public someMethod1(): void {
    }

    // TODO: What is the proper type for propertyName?
    protected method(propertyName: keyof this): void {
        const propertyValue: any = this[propertyName];
        // ...
    }
}

export class DerivedClass extends BaseClass {
    private someField2: number = 2;

    public get someProperty2(): number {
        return 1;
    }

    public someMethod2(): void {
    }

    public test(): void {
        super.method("someField1"); // Allowed
        super.method("someProperty1"); // Allowed
        super.method("someMethod1"); // Not allowed

        super.method("someField2"); // Allowed
        super.method("someProperty2"); // Allowed
        super.method("someMethod2"); // allowed

        super.method(""); // Not allowed
        super.method("qwerty"); // Not allowed
    }
}

一个过滤掉函数的版本,但只能在类外部使用,将使用条件类型:

type FilterFucntion<T> = { [P in keyof T]-?: T[P] extends Function ? never : P }[keyof T]
export abstract class BaseClass {
    public someField1: number = 2;

    public get someProperty1(): number {
        return 1;
    }

    public someMethod1(): void {
    }

    public method(propertyName: FilterFucntion<this>): void {
        const propertyValue: any = this[propertyName];
        // ...
    }
}

export class DerivedClass extends BaseClass {
    public someField2: number = 2;

    public get someProperty2(): number {
        return 1;
    }

    public someMethod2(): void {
    }
}
function test(): void {
    const o = new DerivedClass()
    o.method("someField1"); // Allowed
    o.method("someProperty1"); // Allowed
    o.method("someMethod1"); // Not allowed

    o.method("someField2"); // Allowed
    o.method("someProperty2"); // Allowed
    o.method("someMethod2"); // Not allowed

    o.method(""); // Not allowed
    o.method("qwerty"); // Not allowed
}