Reputation: 4245
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?
Upvotes: 131
Views: 132605
Reputation: 72
I had this issue, and I solved it in two ways that I will explain in below.
First Solution:
interface Person {
firstName?: string
lastName?: string
readonly age?: number
fullName(): string
}
class User implements Person {
private _firstName: string | undefined
private _lastName: string | undefined
constructor(public firstName?:string,public lastName?: string, readonly age?: number){
this._firstName = firstName
this._lastName = lastName
delete this.firstName;
delete this.lastName;
}
fullName(): string {
return this._firstName + " " + this._lastName;
}
introduce(): string{
return this.fullName() + " is " + this.age;
}
}
let Reza= new User("Reza","Hadipour",32);
console.log(Reza.fullName()); // -> Reza Hadipour
console.log(Reza.introduce()); // -> Reza Hadipour is 32
console.log(Reza.firstName); // -> Undefined
In the above code, _lastName and _firstName are private and work correctly, but the problem occurs once you want to delete public properties (this.firstName and this.lastName), The delete method needs optional operand, so to solve this I had to define lastName and firstName as optional properties which you can see in interface and constructor implementations. The biggest problem in this solution is the optional property lets users ignore them which should cause problems in the next. because of optional public properties, I have to define private properties with two string and undefined types.
private _firstName: string | undefined
private _lastName: string | undefined
this is so unreliable implementation!! so, I suggest the second way.
Second Solution
interface Person {
firstName: string
lastName: string
readonly age: number
fullName(): string
}
class User implements Person {
private _firstName: string
private _lastName: string
constructor(public firstName:string,public lastName: string, readonly age: number){
this._firstName = firstName
this._lastName = lastName
this.firstName = this.lastName = "";
}
fullName(): string {
return this._firstName + " " + this._lastName;
}
introduce(): string{
return this.fullName() + " is " + this.age;
}
}
let Reza = new User("Reza","Nikpour",35);
console.log(Reza.fullName()); // -> Reza Nikpour
console.log(Reza.introduce()); // -> Reza Nikpour is 35
console.log(Reza.firstName); // ->
In the second code, I just assets empty value into the this.firstName and this.lastName in the constructor instead of deleting them.
Upvotes: 0
Reputation: 538
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
}
}
Upvotes: 18
Reputation: 21
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$;
}
}
Upvotes: 2
Reputation: 13196
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";
}
}
Upvotes: 140
Reputation: 137
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.
Upvotes: 3
Reputation: 343
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())
}
}
Upvotes: -3
Reputation: 1061
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 = ...
}
Upvotes: 0