Typescript: Use class as interface

In this example parent class is supposed to be abstract and acts as an interface. TypeScript interfaces don't have access modifiers, all interface properties are expected to be public.

When concrete class like Test is used as interface a problem appears, because the implementation cannot implement private properties and cannot override them either.

Both Test and SpecialTest should either implement some abstract class as interface (in this case abstract class can have only public members) or inherit from it (in this case abstract class can have protected members).


Before we get started, when you extend a class, you use the extends keyword. You extend a class, and implement an interface. There are additional notes on this further down the post.

class SpecialTest extends Test {

Also, watch out for string vs String as this will trip you up. Your type annotations should almost certainly be string (lowercase).

And finally, you don't need to manually assign constructor parameters, so the original:

class Test {
    private name: string;

    constructor(name: string) {
        this.name = name;
    }

    // ...
}

Is better expressed as:

class Test {
    constructor(private name: string) {
    }

    // ...
}

Now you can choose from a number of solutions to your problem.

Protected Member

Make the name member protected, then you can use it within sub classes:

class Test {
    protected name: string;

    constructor(name: string) {
        this.name = name;
    }

    getName() {
        return this.name;
    }
    setName(name: string): void {
        this.name = name;
    }
}

class SpecialTest extends Test {
    getName(): string {
        return '';
    }
    setName(name: string): void {
    }
}

Interface

This is the solution that I think best matches your needs.

If you pull the public members into an interface, you should be able to treat both classes as that interface (whether or not you explicitly use the implements keyword - TypeScript is structurally typed).

interface SomeTest {
  getName(): string;
  setName(name: string): void;
}

You could explicitly implement it if you wish:

class SpecialTest implements SomeTest {
    private name: string;

    getName(): string {
        return '';
    }
    setName(name: string): void {
    }
}

Your code can now depend on the interface rather than on a concrete class.

Using a Class as an Interface

It is technically possible to reference a class as an interface, but there are problems ahead of you do this with implements MyClass.

Firstly, you are adding unnecessary complexity for anyone who has to read your code later, including the future you. You are also using a pattern that means you need to be careful about the keyword. Accidental use of extends may cause a tricky bug in the future when the inherited class is changed. Maintainers will need to become hawkeye over which keyword is used. And all for what? To preserve nominal habits in a structural language.

Interfaces are abstract, and less likely to change. Classes are more concrete, and more likely to change. Using a class as an interface ruins the entire concept of depending on stable abstractions... and instead causes you to depend on unstable concrete classes.

Consider the proliferation of "classes as interfaces" throughout a program. A change to a class (let's say we add a method) can inadvertently cause a change to ripple for vast distances... how many parts of the program now reject input, because the input doesn't contain a method that is not even used?

The better alternatives (when there are no access modifier compatibility issues)...

Create an interface out of the class:

interface MyInterface extends MyClass {
}

Or, just don't reference the original class at all in your second class. Allow the structural type system to check compatibility.

Side note... depending on your TSLint config, weak types (such as an interface with only optional types) will trigger the no-empty-interface-Rule.

Specific Case of Private Members

None of these (using a class as an interface, generating an interface from a class, or structural type) solve the problem of the private member. This is why the solution that solves the real problem is to create an interface with the public members on.

In the specific case of private members, such as in the question, let's think about what will happen if we continue with the original pattern? The natural outcome will be that to preserve the pattern of using the class as an interface, the visibility of members will be changed, like this:

class Test {
    public name: string;

    constructor(name: string) {
        this.name = name;
    }

    getName() {
        return this.name;
    }
    setName(name: string): void {
        this.name = name;
    }
}

And now we are breaking the more established principles of object-orientation.


To extract an interface from a class, that will only contain the public properties and not all the implementation details of the class you can use the keyof operator together with the Pick<Type, Keys> utilities.

class Test {
  foo: any;
  private bar: any;
}

class SepcialTest implements Pick<Test, keyof Test> {
  foo: any;
}

Pick<Type, Keys> Constructs a type by picking the set of properties Keys from Type.

[...] keyof T, the index type query operator. For any type T, keyof T is the union of known, public property names of T.