Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any semantic difference between interfaces and type aliases?

Tags:

typescript

I can do most of the same thing with interfaces and type aliases.

For example

Classes can implement either interfaces or type aliases

interface Shape {
    area(): number;
}

type Perimeter = {
    perimeter(): number;
}

class Rectangle implements Shape, Perimeter {
}

They can be combined to create new interfaces/type aliases

interface A {
    a: string;
}

type B = {
    b: string
}

interface C extends B {
    c: string;
}

type D = A & {
    d: string;
}

Is there a semantic difference between the interfaces and type annotations?

like image 501
user1283776 Avatar asked Feb 05 '19 14:02

user1283776


2 Answers

There are technical differences between interface and type that are well-described here.

However, for the cases where a type and an interface can both be used, there is no semantic difference at all.

About interface inheritance and type intersection in TypeScript

In TypeScript, the hierarchy between interfaces is just a way to define interfaces. But once they are defined, there is no real parent-child relationships between interfaces. For example:

interface Named {
    name: string
}
interface Person extends Named {
    age: number
}
interface Animal {
    name: string
    age: number
}

Here Person and Animal are the same type. Once they are defined, they will be processed exactly the same way by the compiler when other code use them:

function useNamed(named: Named) {
}
let p: Person = /* ... */
let a: Animal = /* ... */
useNamed(p) // OK
useNamed(a) // also OK, because if 'Animal' is compatible with
            // `Named` then it is a `Named`

That's why the same type can also be created using an intersection type:

type Engine = Named & {
    age: number
}

From the specification:

Intersection types represent values that simultaneously have multiple types. A value of an intersection type A & B is a value that is both of type A and type B. (source: TypeScript Specification)

Our Engine type is both a Named and the additional definition: it is semantically the same thing as interface inheritance. And Engine type here is exactly the same type as Person and Animal.

like image 195
Paleo Avatar answered Oct 20 '22 14:10

Paleo


Interfaces can extend other interfaces and can also *implement classes. Interfaces can also take advantage of declaration merging:

type A = {
  a: string;
}

type A = { // <-- error: duplicate identifier A
  b: string;
}

interface A {
  a: string;
}

interface A { // <-- okay, A is { a: string, b: string } 
  b: string; 
}

edit: changed extend to *implement

edit 2: intersections are not the same as extending. Consider the following:

interface A {
  num: number;
  str: string; 
}

type B = A & { // <-- this is okay, but overwrites the num property type to number & string
  arr: any[];
  num: string;
}

interface C extends A { // <-- error: C incorrectly extends A
  num: string;
}

edit 3: another potentially significant difference for some people (though not necessarily a semantic difference) is that types are enumerated in tool tips (at least in vscode) but interfaces are not.

like image 44
Robbie Milejczak Avatar answered Oct 20 '22 14:10

Robbie Milejczak