代码之家  ›  专栏  ›  技术社区  ›  Alexander Abakumov

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

  •  2
  • Alexander Abakumov  · 技术社区  · 6 年前

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

    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 作为一种 方法() 的参数。但在这种情况下,任何字符串都可以传入,编译器无法验证它是否是现有属性的名称。

    "someMethod1" , "someMethod2"

    应该是哪种类型 propertyName BaseClass

    1 回复  |  直到 6 年前
        1
  •  1
  •   Titian Cernicova-Dragomir    6 年前

    你几乎可以实现你想做的事。您可以使用多态 this 键入以引用当前类(因此它将在重写类中表示派生类,在基类中表示基类)。我们也可以使用 keyof 获取类型的键(在本例中为 类型)。

    • 基奥夫
    • keyof将返回所有属性。虽然可以过滤掉函数,但这在类内部不起作用(因为 本质上是一个自由类型参数,它是完全已知的,因为它可以是这个类或派生类,我们需要条件类型来进行过滤,除非类型完全已知,否则无法重新求解。

    守则:

    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
    }