An interface and a type alias both give a name to a type.
Neither produces any code. They overlap for object types and differ in a few important
ways. Members are described under
object types.
Declarations
InterfaceDeclaration:
interface Name [TypeParameters] [extends TypeReference {, TypeReference}] ObjectType
TypeAliasDeclaration:
type Name [TypeParameters] = Type ;
interface User {
name: string;
age?: number;
}
type ID = string | number;
type Pair<T> = [T, T];
type Callback = (error: Error | null) => void;
extends
An interface can extend one or more object types: interfaces, classes, or aliases of object types. It may not extend a union, a primitive or a type parameter. A member redeclared in the child must be assignable to the parent’s. If two parents declare a member with different types, the child must redeclare it with a compatible type.
interface Named { name: string }
interface Aged { age: number }
interface Person extends Named, Aged { email: string }
type Alias = { id: number };
interface WithId extends Alias { label: string } // fine: an alias of an object type
interface A { x: number }
interface B { x: string }
interface C extends A, B {} // error: x conflicts
interface D extends A { x: string } // error: string does not fit number
type Union = { a: 1 } | { b: 1 };
interface E extends Union {} // error
extends reports conflicts at the declarationAn intersection of the same two types is not an error: the property simply becomes the
intersection of its types,
often never.
Declaration merging
Declarations with the same name in the same scope can combine into one (Handbook: Declaration Merging). A declaration creates a namespace, a type, a value, or several of these:
| Declaration | Namespace | Type | Value |
|---|---|---|---|
namespace | Yes | Yes | |
class | Yes | Yes | |
enum | Yes | Yes | |
interface | Yes | ||
type | Yes | ||
function | Yes | ||
let, const, var | Yes |
Merging interfaces
All declarations of an interface become one. Type parameters must be identical. Non-function members must be unique, or have the same type. Function members with the same name become overloads: each later declaration’s overloads come first, and within a declaration the order is kept. The exception is a signature with a parameter of a single string literal type, which moves to the top.
interface Box { width: number }
interface Box { height: number }
const b: Box = { width: 1, height: 2 };
interface Bad { x: number }
interface Bad { x: string } // error
interface Cloner { clone(a: Dog): Dog; }
interface Cloner { clone(a: Cat): Cat; }
// merged: clone(a: Cat): Cat; then clone(a: Dog): Dog
Namespaces and other merges
Namespaces with the same name combine their exported members; non-exported members stay private to their own block. A namespace may also merge with a class, function or enum, and must come after it, adding static members. A class and an interface of the same name merge. Two classes do not merge, and a class cannot merge with a variable.
function greet() {}
namespace greet { export const prefix = "Hello"; }
greet.prefix;
enum Colour { Red }
namespace Colour { export function mix() {} }
Colour.mix();
class Point { x = 0 }
interface Point { y: number } // adds a member with no implementation
A module can also add to another module and to the global scope, through augmentation.
Interface or type alias
interface | type | |
|---|---|---|
| Object types | Yes | Yes |
| Unions, primitives, tuples, function types | Function types only, as a call signature | Yes |
| Mapped and conditional types | No | Yes |
| Merges with itself | Yes | No: a duplicate is an error |
extends | Yes | Uses & |
| Conflict on extending | An error at the declaration | With &, the member reduces, usually to never |
| Implicit index signature | No | Yes |
Used by implements | Yes | If it is an object type |
The implicit index signature means an object type alias can be assigned to
Record<string, unknown>, but an interface cannot, since the interface
could later be merged with more members.
type TObj = { a: string };
interface IObj { a: string }
const t: TObj = { a: "" };
const i: IObj = { a: "" };
const r1: Record<string, string> = t; // fine
const r2: Record<string, string> = i; // error: no index signature
An alias may be recursive in the ways described under Generics.