1

I am new in TypeScript, could anyone help me to figure it out the best practices with implementing interface in class? Because when I tried to following the Docs (Class Heritage), I caught in some problem like so:

  1. declare interface
interface Notification {
    message: string;
    send(msg?: string): void;
}
  1. implements interface in class with constructor()
class Notifier implements Notification {
    constructor(
        public message: string,
    ){}

    send(customMsg?: string): void {
        if (customMsg) {
            console.log(customMsg);
        } else {
            console.log(this.message);
        }
    }
}
  1. example, using class
const hello = new Notifier("hello");
hello.send();
hello.send("Alert!");

Unfortunately, I found an error message like the following:

Class 'Notifier' incorrectly implements interface 'Notification'. Type 'Notifier' is missing the following properties from type 'Notification': actions, badge, body, data, and 19 more.

Could anyone tell me what is my wrong? Thanks in advance!

Penny Liu
  • 15,447
  • 5
  • 79
  • 98
DSPdav
  • 145
  • 1
  • 7
  • 1
    Aside: I am changing your `message` property from `private` to `public` or you will hit a new error after you resolve the current one. Since you are not asking about `private` vs `public`, this issue is therefore outside the scope of the question and we should clear things up by using `public`, as my answer below does. – jcalz Apr 27 '21 at 15:47

1 Answers1

1

Your problem is that the TypeScript standard library already includes a globally-scoped interface named Notification corresponding to the Notification interface from the Web Workers API. Because of this, your Notification interface definition is just merging additional members into it. This is obviously not what you intend.

The fix here is either to rename your interface to something else like MyNotification, or create a module or namespace that creates a new naming scope for your code:

// use of "export" makes your code a module
export interface Notification { 
    message: string;
    send(msg?: string): void;
}

or

namespace MyNamespace {
  export interface Notification {
    message: string;
    send(msg?: string): void;
  }
}

And then you should be able to refer to it later. If you use the namespace approach, you'll get this:

class Notifier implements MyNamespace.Notification {
  constructor(
    public message: string, 
  ) { }

  send(customMsg?: string): void {
    if (customMsg) {
      console.log(customMsg);
    } else {
      console.log(this.message);
    }
  }
}


const hello = new Notifier("hello");
hello.send();
hello.send("Alert!");

Which works as desired.

Playground link to code

jcalz
  • 264,269
  • 27
  • 359
  • 360
  • Thank you so much! I don't realize it and it is work when I change the name of that interface to `MyNotification`. – DSPdav Apr 27 '21 at 15:49
  • But, how about using private access modifiers in constructor while implementing an interface? Since in your example, we must change `message` property to public. – DSPdav Apr 27 '21 at 15:50
  • 1
    That's a different question, isn't it? If I have a `MyNotification` object, I should be allowed to access its `message` property, according to your interface definition. If `Notifier` makes `message` a `private` property, then I can't do that. Just like you couldn't make it a `number` if it is supposed to be a `string`. Therefore, making it `private` causes `Notifier` not to be a valid `MyNotification`. You can have private properties in `Notifier`, but they cannot conflict with properties from `MyNotification`. – jcalz Apr 27 '21 at 15:55
  • A comment section is not a good place to discuss this subsequent issue. You should consider doing more research to look for existing questions like [this one](https://stackoverflow.com/questions/37791947/how-to-define-a-private-property-when-implementing-an-interface-in-typescript), and if you can't find an answer, consider posting a new question. Good luck! – jcalz Apr 27 '21 at 15:57