5

In the company in which I work, it is customary to model entities using Interfaces when developing with Typescript.

To me, it seems much more effective to model entities with classes and then create instances wherever an object of that type is needed.

When I talked about it with my colleagues including the CTO, I asked what is the best way to create new object characterized by some interface, and was told to create a function that returns an object containing all the fields matching to that interface initialized to null or undefined.

To me, this just looks like a constructor.

I would love to hear an explanation of why using that technique is better than generating classes and instances, or what are the best practices for that scenario.

Also, are typescript interfaces not standard OOP interfaces? is the name misleading me?

3
  • To me, this just looks like a constructor. -- Technically, it is a Factory Method. Jul 10 '19 at 15:37
  • @RobertHarvey Then why use interfaces+factory methods over classes+constructors?
    – Gulzar
    Jul 14 '19 at 7:19
  • Did you read the article I linked? It explains why. Jul 14 '19 at 15:18
2

Classes in general don't compose as well, are less descriptive and less reusable. Ex:

// only email or username is required
class User {
  id: string
  password: string
  username?: string
  email?: string
}

// multiple signatures, not tied to user model
declare function logIn(password: string, username: string): void
declare function logIn(password: string, email: string): void
declare function find(userId: string): User

vs

// reusable across codebase
interface Id {
  id: string
}

// more accurately describes data
type Credentials = {
  username: string
  password: string
} | {
  email: string
  password: string
}

// concise composition
type User = Id & Credentials

// always matches user model
declare function logIn(creds: Credentials): void
declare function find(id: Id): User
0

In TypeScript you can have multiple constructor overloads, but just one implementation

    constructor();
    constructor(obj: ISomething); 
    constructor(obj?: any) {    
       // The implemenation
    }

Therefor if you have the need of multiple creation ways, then multiple factory methods would be the way to go. Personally i move them as static methods into the class. That way everything is at one place.

Personally i always use classes for my entities. It allows me to ensure that the data follows specific constraints (like ranges). It allows me to define the allowed operations. And its possible to ensure initial values. When using interfaces directly, then this is not possible. And i can´t ensure that the factory method was used. As a result if i want to change the creation process later, i have to check the whole code that nowhere the entity was created without the factory method.

The example of @AlfredYoung could be partially solved with a mixture of classes and interfaces.

interface Credentials {
  password: string
  username?: string 
}

interface ID {
  id: string
}

class User implements ID, Credentials {
  id: string
  password: string
  username?: string
  email?: string
}

declare function logIn(creds: Credentials): void
declare function find(userId: ID): User
0

The essential concept of an "interface" is that it is "a minimal, but language-enforceable commitment." Each class which implements an interface can of course implement more, but the language will verify that it does not implement less, nor that its stated implementation varies in any way from "the minimal contract that was committed-to by all."

Of course, the benefit is that: this is something that the programming-language itself can meaningfully enforce ... and, most importantly, "at compile time." So, if you made a mistake, the language will catch it ... now.

(P.S.: Doesn't matter which programming language we are right now talking about ...)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.