Type vs Interface
Tags: typescript, beginners, webdev
Published: 2024-06-26 05:34:26

In TypeScript development, choosing between type and interface can be confusing. Both are essential for defining object shapes and types, but knowing when to use each can be tricky. 
Developers often wonder: When should I use `type` instead of `interface`?
How can I extend existing types? What about defining complex types, unions, or tuples? This blog will clarify these concepts, providing guidelines and examples to help you choose the right tool for your TypeScript projects, ensuring cleaner and more efficient code.

- 1. Basic Type and Interface Declarations.

**Type**
```typescript
type User = {
	name: string;
	email: string;
};
// Notation Suggestion:
// Use TUser for type declarations.
```
**Interface**
```typescript
interface User {
	name: string;
	email: string;
};
// Notation Suggestion:
// Use IUser for interface declarations.
```
- 2. Extending Types and Interfaces.

To extend a type in TypeScript, you use an intersection (&) to combine it with additional properties. For interfaces, you use the extends keyword to inherit properties from another interface.

```typescript
type User = {
	name: string;
	email: string;
};

type SuperUser = User & {
	roleAccess: string;
};
```

```typescript
interface User {
	name: string;
	email: string;
};

interface SuperUser extends User {
	roleAccess: string;
};
```
### Reasons to Prefer type Over interface.

**1. Type Aliases Can Define Non-Object Types.**

- Type aliases are more versatile as they can define primitives, union types, and other non-object types.

```typescript
type Status = 'Success' | 'Error' | 'Warning';
let status: Status = 'Success';
```

- This is invalid with interfaces:

```typescript
interface Status = 'Success' | 'Error' | 'Warning'; // Invalid
```
- Workaround with interfaces, but less intuitive:

```typescript
interface Response {
    Status: 'Success' | 'Error' | 'Warning';
}
const response: Response = {
    Status: 'Success'
};
```

**2. Union Types**

- You cannot create union types with interfaces.
```typescript
type Status = string | string[];

const status: Status = ['Success', 'Error'];
```

- Invalid with interfaces
```typescript
interface Status = string | string[]; // Invalid
```

**3. Omitting Fields**
- Easily omit fields in a new type
```typescript
type User = {
    name: string;
    email: string;
    createdAt: string;
};

type Guest = Omit<User, 'name' | 'email'>;
```

- Interface equivalent looks cumbersome
```typescript
interface User {
    name: string;
    email: string;
    createdAt: string;
}

interface Guest extends Omit<User, 'name' | 'email'> {}; // Works but not pretty
```
**4. Tuples**
- Types handle tuples more elegantly.
```typescript
type Response = [number, string];

const successResponse: Response = [200, 'Success'];
```

- With interfaces
```typescript
interface Response extends Array<number | string> {
    0: number;
    1: string;
}
```
**5. Using Existing Types**

- You can easily create new types based on existing objects.
```typescript
const aiServices = {
    id: 1,
    title: 'AI Development',
    description: `Lorem ipsum...`,
    tags: ['#ai', '#artificialIntelligence']
};

type Services = typeof aiServices;

const services: Services[] = [{...}];
```

- Using as const for literal types
```typescript
const successResponse = {
    status: 'Success',
    code: 200,
    data: [{...}]
} as const;
```

### Advantages of Interfaces

**1. Declaration Merging**

- Interfaces can be merged, allowing you to add fields to an existing interface.
 ```typescript
interface User {
    name: string;
    email: string;
}

interface User {
    createdAt: string;
}

const user: User = {
    name: 'Prashant',
    email: '023prashantsharma@gmail.com',
    createdAt: '01/01/2024'
};
```

**2. Class Implementation**

- Interfaces are ideal for class-based models and allow for more descriptive declarations
```typescript
interface Student {
    name: string;
    age: number;
    className: string;
}

class StudentImpl implements Student {
    name: string;
    age: number;
    className: string;

    constructor(name: string, age: number, className: string) {
        this.name = name;
        this.age = age;
        this.className = className;
    }
}
```

## Conclusion

- When to Use `type`:
For primitive types, union types, tuples, and when needing more flexibility.
To create complex type combinations and utility types like Omit.

- When to Use `interface`:
For object type declarations, especially when extending or implementing classes.
When declaration merging is beneficial.

**Official Recommendations**

1. Interfaces for Better Documentation and Readability:
Interfaces are often preferred for their readability and self-documenting nature.
2. Performance Considerations:
While interfaces may have slight performance advantages in very large codebases, this is typically negligible for most applications.
3. Declaration Merging:
Interfaces can merge, which can be both a feature and a pitfall. It allows augmenting existing types provided by libraries.
By balancing the use of type and interface appropriately, you can leverage the strengths of each to write more effective and maintainable TypeScript code.