111

I'm using TypeScript in my project and I have come across an issue. I'm defining an interface like this:

interface IModuleMenuItem {
    name: string;
}

I want to create a class that implements from this interface but I want the name to be a private property like this:

class ModuleMenuItem implements IModuleMenuItem {
    private name: string;
}

I'm getting the following error:

Class ModuleMenuItem incorrectly implements interface IModuleMenuItem. Property name is private in type ModuleMenuItem but not in type IModuleMenuItem.

How can I define a property as private or protected when implementing an interface?

Flip
  • 6,233
  • 7
  • 46
  • 75
Golan Kiviti
  • 3,895
  • 7
  • 38
  • 63
  • 1
    Interfaces are basically here to define what is public. I'm not sure you have a way to check what is private. – Blackus Jun 13 '16 at 14:14

6 Answers6

127

Interfaces define "public" contracts and as such it doesn't make sense to have protected or private access modifier on interfaces, which are more of a, let's call it, implementation detail. For that reason you can't do what you want with an interface.

If you want to make the property read-only to consumers, but overridable in a subclass then you can do something like this:

interface IModuleMenuItem {
     getName(): string;
}

class ModuleMenuItem implements IModuleMenuItem {
    private name;

    public getName() {
        return name;    
    }

    protected setName(newName : string) {
        name = newName;
    }
}

I think in TypeScript 2.0 (not out yet) you will be able to use the readonly access modifier if you were after initialization-time readonly field - https://basarat.gitbooks.io/typescript/content/docs/types/readonly.html

interface IModuleMenuItem {
     readonly name : string;
}

class ModuleMenuItem implements IModuleMenuItem {
    public readonly name : string;

    constructor() {
        name = "name";
    }
}
Dave Arkell
  • 3,920
  • 2
  • 22
  • 27
Ivan Zlatev
  • 13,016
  • 9
  • 41
  • 50
  • 11
    So whats the deal? I mean its just stupid, It ruins the whole point of interfaces. – Golan Kiviti Jun 14 '16 at 05:51
  • 19
    @Pachu To me that's the whole point of interfaces. It's to define a **public** contract that an implementation "conforms" to and an external component can consume without caring about how it's implemented. What you are after is more enforcing implementation behaviour, which is achieved through abstract base classes in most languages. – Ivan Zlatev Jun 14 '16 at 10:27
  • Would someone please tell this to the creators of TypeScript? Apparently it includes private members when defining interfaces. :( – theMayer May 10 '19 at 21:55
  • 2
    @theMayer It is not required. You can use an abstract class to implement the interface and create another class to extend that class. This method of implementation is known as Composite. Though you would have to use protected instead of private as private cannot be re-implemented. – Aniket Chowdhury Aug 07 '20 at 09:41
  • Added an answer as well. Might as well have. – Aniket Chowdhury Aug 07 '20 at 09:49
  • @IvanZlatev sure, *traditionally*, but javascript, and now typescript, has always been at the forefront of what we should always have been able to do with these language structures. [Here's what I think is a valid usecase for private interface members](https://gist.github.com/Hashbrown777/4c6eeff85dfbea54374f1da2d065b1d3): a PRIVATE contract (interfaces, really, are to keep the programmer in line, and this doesnt in and of itself need to be public). The playground link was too big for StackOverflow, so you can click the link in the comment on that gist if you'd like – Hashbrown May 20 '21 at 12:31
  • The `public readonly` definitely ensures my final goal of preventing changes post-constructor implementation, with basic tree shaking it works just as well as `private` for my use case. – Z. Bagley Aug 24 '21 at 19:25
  • For people who don't understand yet why it's legit and can be useful: https://github.com/microsoft/TypeScript/issues/10302 – Ivan Kleshnin Dec 15 '22 at 05:42
16

I think you can do it like this:

interface IModuleMenuItem {
    name: string
}

class ModuleMenuItem implements IModuleMenuItem {
    private _name: string;
    constructor() {
        this._name = "name";
    }

    get name() {
        // your implementation to expose name
    }

    set name(value) {
        // your implementation to set name         
    }
}
Zach Saucier
  • 24,871
  • 12
  • 85
  • 147
  • 1
    I think this is better since we can use it with getter setter. The accepted solution uses `getProp()` which is fine as well, but getter setter makes the code a bit cleaner. – Moses Aprico Nov 05 '19 at 16:12
  • How is this working for anyone? ```this``` isn't even used in the constructor. – Isaac Pak Jul 23 '20 at 17:37
3

In case of having private fields in class, you need to introduce setter and get methods for that field like so:

export class Model {
    private _field: number;

    get field(): number {
        return this._field;
    }

    set field(value: number) {
        this._field= value;
    }
}

And then create the interface as usual (We can not use private modifier for interface fields) like so:

export interface IModel {
 field: number;
}

Then implement it to our class like so:

export class Model implements IModel {
...
}

TypeScript will understand that this model is implemented correctly the interface as we have introduced set and get method.

Syntax
  • 137
  • 1
  • 6
0

The only way you can have an inner state and assign interface to that instead of class and make that state private

class A{
  private state:IA = ...
}
Mohammad f
  • 961
  • 10
  • 11
0

As an addendum to Syntax's response, there is no need to include a setter. A getter method is all that is needed. This could be used, for example, for read-only variables set at initialization, though I suppose it's better to use the readonly modifier in this case.

interface IModuleMenuItem
{
    name: string;
}

class ModuleMenuItem implements IModuleMenuItem{
    private name$: string;

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

    public get name()
    {
        return this.name$;
    }
}
-3

Use abstract classes instead.

Composition over inheritance.

interface AppInterface {
   app: express.Application
   port: string | number
}

abstract class AbstractApp implements AppInterface {
    app: express.Application
    port: string | number
    constructor(){
        this.app=express()
        this.port=8080
    }
    
    protected defaultMiddlewares(): void {}
}     

class App extends AbstractApp {
    constructor() {
        super()
    }

    protected defaultMiddlewares(): void {
        this.app.use(express.json())
    }
}
Aniket Chowdhury
  • 332
  • 3
  • 13