Type, Declare, and Interface in TypeScript
The difference between type and interface
type
and interface
are two key concepts in TypeScript that allow you to describe the structure of complex data types, such as objects and arrays. While they are similar in some ways, they are also distinct in their use cases and how they are implemented in the language.
Here are the main differences between type
and interface
in TypeScript:
- Syntax: The syntax for defining a type and an interface is different, with
type
being a simpler syntax andinterface
being more flexible and powerful. - Structural typing:
interface
uses structural typing, meaning that two objects are considered compatible if they have the same structure, regardless of their names. On the other hand,type
uses nominal typing, meaning that two objects must have the same name in order to be considered compatible. - Merging:
interfaces
can be merged in TypeScript, which allows you to combine multiple interfaces into a single type.Types
, on the other hand, cannot be merged in the same way. - Declaration:
type
is used for type aliases, whileinterface
is used for interfaces. - Usage:
interfaces
are typically used to define the structure of objects, whiletypes
are used for a wider range of purposes, including type aliases, unions, and intersections.
In general, it's best to use interface
when you want to describe the structure of objects and type
when you need to create more complex data types.
In my project, I always use either type
or interface
within a component. For example, in the code snippet below, the IbpHeadProps
is declared as a type with optional properties size
and className
. This type is then used as the type of the function component's props.
import React from "react";
// Use either `type` or `interface` is fine
type IbpHeadProps {
size?: number;
className?: string;
}
export default function IbpHead({ size, className }: IbpHeadProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size || 50}
height={size || 50}
viewBox="0 0 241.188 139.874"
className={`h-auto ${className || ""}`}
>
...
</g>
</svg>
);
}
In my project, I utilize interface
within my types
folder to provide a more structured type definition
In TypeScript, interfaces are used to provide a more structured type definition. In a project, they are typically stored within a types
folder.
Simple Interface Usage
Here is an example of how you can define and use interfaces in TypeScript:
// types/user.ts
export interface User {
id: number;
name: string;
email: string;
password: string;
}
// types/post.ts
export interface Post {
id: number;
title: string;
content: string;
authorId: number;
}
// types/comment.ts
export interface Comment {
id: number;
content: string;
postId: number;
authorId: number;
}
Each interface defines the shape of a type. For example, User
has id
, name
, email
, and password
properties. These interfaces can be imported and used in other parts of your codebase.
Namespaced Interfaces
You can also wrap your interfaces in a namespace to avoid naming collisions and make the code easier to organize. Here's an example:
declare namespace Types {
// types/user.d.ts
export interface User {
id: number;
name: string;
email: string;
password: string;
}
// types/post.d.ts
export interface Post {
id: number;
title: string;
content: string;
authorId: number;
}
// types/comment.d.ts
export interface Comment {
id: number;
content: string;
postId: number;
authorId: number;
}
}
// main.d.ts
namespace MyApp {
export import User = Types.User;
export import Post = Types.Post;
export import Comment = Types.Comment;
}
In this example, all the interfaces are defined within the Types
namespace. The main.ts
file then re-exports the interfaces under the MyApp
namespace. Now, when you import the interfaces in other parts of your code, you can use MyApp.User
, MyApp.Post
, and MyApp.Comment
.
declare and declared ts extension
In TypeScript, declare namespace
and namespace
have different purposes.
declare namespace
is used to declare a new namespace in a separate .d.ts declaration file, with the intention that this namespace will be merged with a namespace declared elsewhere, either in another .d.ts file, or in the main code. The declare
keyword is used to indicate that this is just a declaration, not a definition.
// types.d.ts
declare namespace MyNamespace {
export interface MyType {
...
}
}
On the other hand, namespace
is used to create a new namespace in the main code. This namespace can contain both types and values, and can be used to organize your code into logical units.
// main.ts
namespace MyNamespace {
export interface MyType {
...
}
export const myValue: MyType = ...;
}
So, in essence, declare namespace
is used to augment existing namespaces from other files, while namespace
is used to create a new namespace from scratch within a single code file.
Here is a comprehensive explanation of the difference between the .d.ts
and .ts
syntax in TypeScript.
Does import have to do with namespace
The import
statement has no relationship with declare namespace
or namespace
in TypeScript. The import
statement has no connection with declare namespace
or namespace
in TypeScript.
They serve different purposes.
the import
statement is not used with declare namespace
. The declare namespace
is a way to declare a namespace in TypeScript, which allows you to group together values, functions, and objects under a common name. This can be useful for organizing your code and avoiding naming collisions with other code in your application.
The import
statement is used to include exports from another module into your current module, while declare namespace
is used to define a namespace in TypeScript. The two are not related and are used for different purposes.
Here is an example of a declare namespace
:
declare namespace MyModule {
export interface MyInterface {
// properties and methods
}
export function myFunction(): void;
}
And here is an example of an import
statement:
import { myModuleFunction } from './myModule';
How to extend type
The above code examples demonstrate two different ways to extend a type in TypeScript: using the type
keyword, and using the interface
keyword.
In the first example, the type CategoryWithProductsProps
is being defined as a combination of two existing types, Category.GetCategoryResponse
and an object with a property products
that is an array of Product
objects. This results in a new type that has all of the properties of Category.GetCategoryResponse
and an additional property products
of type Product[]
.
In the second example, the interface
keyword is used to extend the GetProductResponse
type. The GetProductResponseWithCategories
interface is defined as having all of the properties of the GetProductResponse
type, with the addition of a property categories
of type number[]
.
Both the type
and interface
keywords can be used to extend a type in TypeScript, but the interface
keyword is more commonly used for this purpose, as it provides more flexibility in defining the properties and methods of the new type.
// for reserve word `type`
type CategoryWithProductsProps = Category.GetCategoryResponse & {
products?: Product[];
};
// for reserve word `interface`
interface GetProductResponseWithCategories extends GetProductResponse {
categories: number[];
}
When two key types are identical, you can simply use a blank type instead of repeating it. For instance, the ShippingAddress
interface in the following example is the same as the BillingAddress
interface
export interface ShippingAddress extends BillingAddress {}
export interface BillingAddress {
first_name: string;
last_name: string;
company: string;
street_1: string;
street_2?: string | undefined;
city: string;
state: string;
zip: string;
country: string;
country_iso2: string;
phone: string;
email: string;
form_fields?: any[];
}