简体   繁体   中英

Why does TypeScript complain about my implementation of an abstract class member?

Below is my custom type:

type KeyMap<T> = { [K in keyof T]: keyof Pick<T, K> }

Followed by a simple interface to use with the custom type:

interface MyInterface {
    a: string;
    b: string;
}

When variable of my type is defined outside of a class, TypeScript is happy:

const member: KeyMap<MyInterface> = {
    a: 'a',
    b: 'b'
}

But when I define an abstract class with an abstract member of type KeyMap and try to implement it, it does not work. For example:

abstract class BaseClass<T> {
    protected abstract member: KeyMap<T>
    protected abstract method: () => void;
}

class DerivedClass extends BaseClass<MyInterface> {
    // TypeScript reports that property 'a' is not compatible with '"a"'
    protected member = { 
        a: 'a', 
        b: 'b'
    };

    protected method = () => console.log('');
}

Defining member directly in the abstract or derived class seems to work. For example:

abstract class BaseClass {
    public member: KeyMap<MyInterface> = {
        a: 'a', 
        b: 'b'
    }
    protected abstract method: () => void;
}

class DerivedClass extends BaseClass {
    public derivedMember: KeyMap<MyInterface> = {
        a: 'a', 
        b: 'b'
    }

    protected method = () => console.log('');
}

As does changing member to be a different type:

abstract class BaseClass<T> {
    protected abstract member: { c: string, d: string };
    protected abstract method: () => void;
}

class DerivedClass extends BaseClass<MyInterface> {
    protected member = { 
        c: 'c', 
        d: 'd'
    };

    protected method = () => console.log('');
}

Why does TypeScript report the implementation of member in the derived class as an error when it works outside of the class and when it's not marked as abstract?

TypeScript Playground Link

Class members are types without regard to what is in the base class, and only then is the derived class checked for compatibility with the base class. Since the member is typed based on the initialization value, typescript will not use literal types for property types (there are only certain places TS will not widen a literal type and this is not one of them)

The best think you can do is use an explicit type annotation for the member as you already say you tried in your question:

class DerivedClass extends BaseClass<MyInterface> {
    protected member: KeyMap<MyInterface> = { 
        a: 'a', 
        b: 'b'
    };

    protected method = () => console.log('');
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM