Trong bài viết này, chúng ta sẽ tìm hiểu về các Utility Types trong TypeScript.
Utility Types giúp chúng ta biến đổi các kiểu dữ liệu một cách linh hoạt và tiện lợi.
Partial
là một Utility Type cho phép chúng ta tạo ra một kiểu dữ liệu mới từ một kiểu dữ liệu đã có, nhưng tất cả các thuộc tính trong kiểu dữ liệu mới này đều trở thành optional.
Ví dụ:
interface Todo {
title: string;
description: string;
}
function updateTodo(todo: Partial<Todo>) {
// ...
}
Trong ví dụ trên, Partial<Todo>
sẽ tạo ra một kiểu dữ liệu mới với tất cả các thuộc tính (title
, description
) đều trở thành optional.
Ngược lại với Partial
, Required
sẽ tạo ra một kiểu dữ liệu mới từ một kiểu dữ liệu đã có, nhưng tất cả các thuộc tính trong kiểu dữ liệu mới này đều trở thành bắt buộc.
Ví dụ:
interface Todo {
title?: string;
description?: string;
}
function createTodo(todo: Required<Todo>) {
// ...
}
Trong ví dụ trên, Required<Todo>
sẽ tạo ra một kiểu dữ liệu mới với tất cả các thuộc tính (title
, description
) đều trở thành bắt buộc.
Readonly
tạo ra một kiểu dữ liệu mới từ một kiểu dữ liệu đã có, nhưng tất cả các thuộc tính trong kiểu dữ liệu mới này đều không thể thay đổi giá trị.
Ví dụ:
interface Todo {
title: string;
description: string;
}
let todo: Readonly<Todo> = {
title: "Learn TypeScript",
description: "Learn TypeScript's utility types"
};
todo.title = "New title"; // Error: Cannot assign to 'title' because it is a read-only property
Trong ví dụ trên, Readonly<Todo>
tạo ra một kiểu dữ liệu mới mà tất cả các thuộc tính (title
, description
) đều không thể thay đổi giá trị.
Record
là một Utility Type cho phép chúng ta tạo ra một kiểu dữ liệu mới, trong đó mỗi thuộc tính của kiểu dữ liệu đó đều có cùng một kiểu dữ liệu nhất định.
Ví dụ về cách sử dụng Record
:
type CarRecord = Record<string, string>;
const car: CarRecord = {
make: 'Toyota',
model: 'Camry',
year: '2020'
};
Trong ví dụ trên, CarRecord
là một kiểu dữ liệu mới, trong đó mỗi thuộc tính đều là kiểu string
.
Pick
là một Utility Type cho phép chúng ta tạo ra một kiểu dữ liệu mới từ một kiểu dữ liệu đã có, nhưng chỉ chọn lấy một số thuộc tính nhất định.
Ví dụ về cách sử dụng Pick
:
type Car = {
make: string;
model: string;
year: string;
};
type CarMakeAndModel = Pick<Car, 'make' | 'model'>;
const car: CarMakeAndModel = {
make: 'Toyota',
model: 'Camry'
};
Trong ví dụ trên, CarMakeAndModel
là một kiểu dữ liệu mới, được tạo ra từ Car
, nhưng chỉ chọn lấy thuộc tính make
và model
.
Omit
cũng giống như Pick
, nhưng ngược lại, nó cho phép chúng ta tạo ra một kiểu dữ liệu mới từ một kiểu dữ liệu đã có, nhưng loại bỏ đi một số thuộc tính nhất định.
Ví dụ về cách sử dụng Omit
:
type Car = {
make: string;
model: string;
year: string;
};
type CarWithoutYear = Omit<Car, 'year'>;
const car: CarWithoutYear = {
make: 'Toyota',
model: 'Camry'
};
Trong ví dụ trên, CarWithoutYear
là một kiểu dữ liệu mới, được tạo ra từ Car
, nhưng loại bỏ đi thuộc tính year
.
Exclude
là một Utility Type giúp loại bỏ một số kiểu dữ liệu khỏi một kiểu dữ liệu khác.
Ví dụ:
type T = Exclude<"a" | "b" | "c", "a">;
// Kết quả: "b" | "c"
Trong ví dụ trên, chúng ta đã loại bỏ kiểu "a"
khỏi kiểu "a" | "b" | "c"
, kết quả thu được là "b" | "c"
.
Extract
là một Utility Type giúp trích xuất một số kiểu dữ liệu từ một kiểu dữ liệu khác.
Ví dụ:
type T = Extract<"a" | "b" | "c", "a" | "b">;
// Kết quả: "a" | "b"
Trong ví dụ trên, chúng ta đã trích xuất kiểu "a" | "b"
từ kiểu "a" | "b" | "c"
, kết quả thu được là "a" | "b"
.
NonNullable
là một Utility Type giúp loại bỏ null
và undefined
khỏi một kiểu dữ liệu.
Ví dụ:
type T = NonNullable<"a" | null | undefined>;
// Kết quả: "a"
Trong ví dụ trên, chúng ta đã loại bỏ null
và undefined
khỏi kiểu "a" | null | undefined
, kết quả thu được là "a"
.
ReturnType
là một Utility Type giúp chúng ta lấy kiểu dữ liệu mà một hàm trả về.
Ví dụ:
function greet(): string {
return "Hello, world!";
}
type Greeting = ReturnType<typeof greet>; // string
Trong ví dụ trên, ReturnType<typeof greet>
sẽ trả về kiểu string
vì hàm greet
trả về một chuỗi.
Parameters
là một Utility Type giúp chúng ta lấy kiểu dữ liệu của các tham số của một hàm.
Ví dụ:
function greet(name: string, age: number): string {
return `Hello, ${name}. You are ${age} years old.`;
}
type Params = Parameters<typeof greet>; // [string, number]
Trong ví dụ trên, Parameters<typeof greet>
sẽ trả về một mảng kiểu [string, number]
vì hàm greet
nhận vào hai tham số là name
kiểu string
và age
kiểu number
.
ConstructorParameters
là một Utility Type giúp chúng ta lấy kiểu dữ liệu của các tham số của một constructor.
Ví dụ:
class Person {
constructor(name: string, age: number) {}
}
type Params = ConstructorParameters<typeof Person>; // [string, number]
Trong ví dụ trên, ConstructorParameters<typeof Person>
sẽ trả về một mảng kiểu [string, number]
vì constructor của class Person
nhận vào hai tham số là name
kiểu string
và age
kiểu number
.
ThisParameterType
là một Utility Type giúp chúng ta lấy kiểu của tham số this
trong một hàm.
Ví dụ:
function toInt(this: string) {
return parseInt(this, 10);
}
type StringToInt = ThisParameterType<typeof toInt>;
Trong ví dụ trên, StringToInt
sẽ có kiểu là string
vì this
trong hàm toInt
có kiểu là string
.
OmitThisParameter
là một Utility Type giúp chúng ta loại bỏ kiểu của tham số this
trong một hàm.
Ví dụ:
function toInt(this: string) {
return parseInt(this, 10);
}
type FunctionWithoutThis = OmitThisParameter<typeof toInt>;
Trong ví dụ trên, FunctionWithoutThis
sẽ là một hàm không có tham số this
.
ThisType
là một Utility Type giúp chúng ta chỉ định kiểu của this
trong một object literal.
Ví dụ:
type ObjectWithThis = {
hello: string;
greet(this: ObjectWithThis): string;
};
let obj: ThisType<ObjectWithThis> = {
hello: "world",
greet() {
return `Hello, ${this.hello}`;
},
};
Trong ví dụ trên, ThisType<ObjectWithThis>
sẽ tạo ra một object có kiểu this
là ObjectWithThis
.