function vs property in interface

There are two ways to define a method in an interface.
  • Declare as a property whose type is function
interface Logger {
    log: (message: string) => void;
}
  • Declare as a normal function
interface Logger {
    log(message: string): void;
}

Differences

  1. If the method is declared as a interface function, then it's possible for you to add more overload versions.
    interface Logger {
        log(message: string): void;
    }
    
    // In other places
    interface Logger {
        log(message: string, level: string): void;
    }
    Declaring method as a property, on the other hand, prevents you from duplicating the property declarations which have different types:
    interface Logger {
        log: (message: string) => void;
    }
    
    // Does not work
    interface Logger {
        log: (message: string, level: string) => void;
    }
  2. The readonly modifier only has effect with the property declaration.
    interface Person {
        firstName: string;
        lastName: string;
    
        readonly fullName: () => string;
    
        // Doesn't work
        // readonly fullName(): string;
    }
  3. TypeScript generates different output for a class that implements the interface methods.
    Assume that we have a class ConsoleLogger that simply logs the message in the Console window.
    For the first approach:
    interface Logger {
        log: (message: string) => void;
    }
    
    class ConsoleLogger implements Logger {
        log = (message: string) => {
            console.log(message);
        }
    }
    
    // Generated JavaScript code:
    // 
    // class ConsoleLogger {
    //    constructor() {
    //        this.log = (message) => {
    //            console.log(message);
    //        };
    //    }
    // }
    For the second approach:
    interface Logger {
        log(message: string): void;
    }
    
    class ConsoleLogger implements Logger {
        log(message: string) {
            console.log(message);
        }
    }
    
    // Generated JavaScript code:
    //
    // class ConsoleLogger {
    //    log(message) {
    //        console.log(message);
    //    }
    // }
    Looking at the generated JavaScript codes, you'll see the different outputs.
    The first approach produces a property log in the constructor. It means that log will be created every time you create a new instance of class.
    While the second approach produces the log method, and it exists in all instances of class. The log method also is a member of class prototype, so we can extend the class to override the method if needed:
    class ConsoleLogger implements Logger {
        log(message: string) {
            console.log(message);
        }
    }
    
    class ConsoleLoggerWithColor extends ConsoleLogger {
        // Override the `log` method
        log(message: string) {
            // Display the message in white color and blue background area
            console.log("%c%s", 'color: white; background: blue', message);
        }
    }
    See the differences between declaring methods in class constructor and prototype for more details.
Hit the Subscribe button for the latest news on my tools. No spam. Ever!
© 2020 Nguyen Huu Phuoc. All rights reserved