What are types, interfaces, and type annotations in TypeScript?

Updated Feb 20, 2026

Short answer

Types, interfaces, and type annotations are TypeScript features that add static type checking to JavaScript. A type annotation explicitly declares the expected type of a variable, function parameter, or return value, while type aliases and interface declarations define reusable custom types. They help catch errors early, improve code readability, and provide better tooling support through autocomplete and refactoring.

Deep explanation

TypeScript is a superset of JavaScript that adds a static type system. JavaScript determines types at runtime, but TypeScript checks types during development and compilation.

Type Annotations

A type annotation tells TypeScript what type a value should have. It is written after a variable, parameter, or return value using a colon (:).

Example:

TypeScript
let username: string = "Alice";
let age: number = 25;
let isActive: boolean = true;

If a value of the wrong type is assigned, TypeScript reports an error:

TypeScript
let age: number = "twenty";
// Error: Type 'string' is not assignable to type 'number'

Type annotations can also be used with functions:

TypeScript
function add(a: number, b: number): number {
return a + b;
}

Here:

  • a: number specifies the parameter types.
  • b: number specifies the parameter types.
  • : number after the parentheses specifies the return type.

TypeScript can often infer types automatically:

TypeScript
let message = "Hello";

TypeScript understands that message is a string, so an annotation is not always required.

TypeScript
let message: string = "Hello";

Both versions are valid, but the inferred version is usually preferred when the type is obvious.

---

Type Aliases (type)

A type alias creates a custom name for an existing type. It is useful for making complex types easier to reuse.

Example:

TypeScript
type User = {
id: number;
name: string;
email: string;
};
const user: User = {
id: 1,
name: "Alice",
email: "alice@example.com"
};

The User type describes the expected structure of a user object.

Type aliases can represent more than objects. They can define unions, primitives, tuples, and other combinations:

TypeScript
type ID = string | number;
let userId: ID;
userId = "abc123";
userId = 456;

A union type means a value can be one of multiple allowed types.

Another example:

TypeScript
type Status = "pending" | "success" | "error";
let requestStatus: Status = "success";

Only the listed string values are allowed.

---

Interfaces

An interface defines the shape of an object. It describes what properties and methods an object should have.

Example:

TypeScript
interface User {
id: number;
name: string;
email: string;
}
const user: User = {
id: 1,
name: "Alice",
email: "alice@example.com"
};

Interfaces are commonly used for object-oriented designs and public APIs.

Interfaces support extension through inheritance:

TypeScript
interface Person {
name: string;
}
interface Employee extends Person {
employeeId: number;
}
const employee: Employee = {
name: "Bob",
employeeId: 1001
};

The Employee interface includes all properties from Person plus its own properties.

---

Differences Between type and interface

For many object shapes, type and interface can be used interchangeably:

TypeScript
interface Product {
id: number;
price: number;
}
type ProductType = {
id: number;
price: number;
};

Both describe the same object structure.

However, there are some differences:

Featureinterfacetype
Object shapesYesYes
Extending typesextendsIntersection (&)
Union typesNoYes
Primitive aliasesNoYes
Declaration mergingYesNo

Example of a type union:

TypeScript
type Result = string | number;

This cannot be represented with an interface.

Example of interface declaration merging:

TypeScript
interface User {
name: string;
}
interface User {
age: number;
}
const user: User = {
name: "Alice",
age: 25
};

TypeScript combines both interface declarations into one.

---

Why Use Types and Interfaces?

They provide several benefits:

  • Early error detection: Problems are caught before running the application.
  • Better editor support: Editors can provide autocomplete and better navigation.
  • Clear contracts: Developers understand what data structures functions expect.
  • Safer refactoring: Changing a type can reveal all affected parts of a codebase.
  • Improved maintainability: Large projects become easier to understand.

---

Optional and Readonly Properties

Types and interfaces can define optional properties using ?:

TypeScript
interface User {
id: number;
name: string;
phone?: string;
}

A User object may or may not have a phone property.

Properties can also be marked as readonly:

TypeScript
interface Config {
readonly apiUrl: string;
}
const config: Config = {
apiUrl: "https://example.com"
};
config.apiUrl = "new-url";
// Error: Cannot assign to 'apiUrl' because it is read-only

---

Real-world example

A frontend application might receive user data from an API. Defining a type ensures that components know what data is available:

TypeScript
interface UserProfile {
id: number;
username: string;
avatarUrl?: string;
roles: string[];
}
function displayUser(user: UserProfile): string {
return `Welcome, ${user.username}`;
}
const profile: UserProfile = {
id: 1,
username: "alex",
roles: ["admin"]
};
console.log(displayUser(profile));

Without the interface, a developer might accidentally use a property that does not exist:

TypeScript
function displayUser(user: UserProfile) {
return user.fullName;
// Error: Property 'fullName' does not exist
}

The type definition acts as a contract between the API data and the application code.

Common mistakes

  • * Using `any` everywhere instead of defining meaningful types.
  • * Adding unnecessary type annotations when TypeScript can already infer the type.
  • * Confusing TypeScript types with JavaScript runtime values
  • types are removed after compilation.
  • * Using interfaces when a union type or complex type composition is required.
  • * Assuming interfaces and type aliases are completely identical in all situations.
  • * Defining overly strict types that make normal code changes difficult.
  • * Forgetting optional properties when an object field may not always exist.
  • * Using type assertions to silence errors instead of fixing incorrect type definitions.

Follow-up questions

  • What is the difference between interface and type in TypeScript?
  • What is type inference in TypeScript?
  • When should you use an interface instead of a type alias?
  • What are union and intersection types?
  • Are TypeScript types available at runtime?

More TypeScript interview questions

View all →