Typescript cung cấp một hệ thống types rất mạnh và phức tạp. Trong bài viết này chúng ta sẽ tìm hiểu một số Advanced Types mà Typescript cung cấp.
Generic types
Generic types là một cách để chúng ta có thể viết code theo hướng Abstraction.
abstract class BaseService<T> {
protected model: Model<T>;
find(): T[] {
return this.model.findAll();
}
findOne(id: number): T {
return this.model.findById(id);
}
}
class BookService extends BaseService<Book> {
constructor(bookModel: Model<Book>) {
super();
this.model = bookModel;
}
}
class AuthorService extends BaseService<Author> {
constructor(authorModel: Model<Author>) {
super();
this.model = authorModel;
}
}
Ví dụ trên là một trong những cách áp dụng Abstraction vào code của các bạn. Trong abstract class BaseService
chúng ta có 2 functions là find
và findOne
. Ở phần khai báo class chúng ta có từ khóa <T>
chính là ký hiệu của Generic
hay còn gọi là Type Parameter
. Bạn có thể dùng bất cứ tên gì để đặt cho Type Paramter
ví dụ: <T>
, <K>
, <Type>
, v.v… Khi chúng ta khai báo BookService
và AuthorService
đều sẽ có 2 hàm giống như abstract class và có kiểu dữ liệu là Book
và Author
tương ứng.
Union Types
Union Types là những types mang tích chất: EITHER OR (hoặc). Để viết Union Types, chúng ta dùng Pipe Symbol (|
)
type Vehicle = 'Bus' | 'Car' | 'Bike';
Intersection Types
Trái ngược với Union Types, Intersection Types là type mà kết hợp nhiều type lại với nhau. Nói cách khác nó mang tích chất: AND (và).
interface Person {
name: string;
}
interface Staff {
company: string;
}
function setStaff(staff: Person & Staff) {}
Utility Types
Typescript cung cấp rất nhiều built-in types mà chúng thường dùng để chuyển đổi type một cách dễ dàng.
Exclude
Exclude<UnionType, ExcludedMembers>
: Lấy những properties của UnionType
không xuất hiện ở ExcludedMembers
interface FirstType {
id: number
firstName: string
lastName: string
}
interface SecondType {
id: number
address: string
city: string
}
type ExcludeType = Exclude<keyof FirstType, keyof SecondType>
// Output; "firstName" | "lastName"
Extract
Extract<Type, Union>:
Lấy những properties xuất hiện ở cả Union
và Type
interface FirstType {
id: number
firstName: string
lastName: string
}
interface SecondType {
id: number
address: string
city: string
}
type ExtractType = Extract<keyof FirstType, keyof SecondType>
// Output: "id"
Readonly
Readonly<T>
: Biến properties của T
thành readonly.
interface ReadonlyType {
id: number
name: string
}
function showType(args: Readonly<ReadonlyType>) {
args.id = 4
console.log(args)
}
showType({ id: 1, name: "Doe" })
// Error: Cannot assign to 'id' because it is a read-only property.
Partial
Partial<T>
: Biến properties của T
thành optional.
interface PartialType {
id: number
firstName: string
lastName: string
}
function showType(args: Partial<PartialType>) {
console.log(args)
}
showType({ id: 1 })
// Output: {id: 1}
showType({ firstName: "John", lastName: "Doe" })
// Output: {firstName: "John", lastName: "Doe"}
NonNullable
NonNullable<T>
: Remove những properties null hoặc undefined từ T
type NonNullableType = string | number | null | undefined
function showType(args: NonNullable<NonNullableType>) {
console.log(args)
}
showType("test")
// Output: "test"
showType(1)
// Output: 1
showType(null)
// Error: Argument of type 'null' is not assignable to parameter of type 'string | number'.
showType(undefined)
// Error: Argument of type 'undefined' is not assignable to parameter of type 'string | number'.
Pick
Pick<Type, Keys>
: Lấy một vài properties từ Type
theo Keys
.
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "title" | "completed">;
const todo: TodoPreview = {
title: "Clean room",
completed: false,
};
Omit
Omit<Type, Keys>
: Remove properties từ Type
theo Keys
.
interface Todo {
title: string;
description: string;
completed: boolean;
createdAt: number;
}
type TodoPreview = Omit<Todo, "description">;
const todo: TodoPreview = {
title: "Clean room",
completed: false,
createdAt: 1615544252770,
};
Record
Record<Keys, Type>
: Tạo ra một type mà properties là có kiểu dữ liệu là Keys và giá trị của những properties đó có kiểu dữ liệu là Type
interface EmployeeType {
id: number
fullname: string
role: string
}
let employees: Record<number, EmployeeType> = {
0: { id: 1, fullname: "John Doe", role: "Designer" },
1: { id: 2, fullname: "Ibrahima Fall", role: "Developer" },
2: { id: 3, fullname: "Sara Duckson", role: "Developer" },
}
// 0: { id: 1, fullname: "John Doe", role: "Designer" },
// 1: { id: 2, fullname: "Ibrahima Fall", role: "Developer" },
// 2: { id: 3, fullname: "Sara Duckson", role: "Developer" }
Conditional types
Conditional types là kiểu dữ liệu được định nghĩa bở một câu điều kiện
condition ? returnTypeIfTrue : returnTypeIfFalse;
type NonNullable<T> = T extends null | undefined ? never : T
Mapped types
Mapped types cho phép chúng ta biến đổi từng kiểu dữ liệu của properties sang một kiểu dữ liệu mới. Để hiểu rõ hơn chúng ta hãy xem ví dụ dưới đây:
type StringMap<T> = {
[P in keyof T]: string
}
function showType(arg: StringMap<{ id: number; name: string }>) {
console.log(arg)
}
showType({ id: 1, name: "Test" })
// Error: Type 'number' is not assignable to type 'string'.
showType({ id: "testId", name: "This is a Test" })
// Output: {id: "testId", name: "This is a Test"}
StringMap<>
sẽ biến đổi kiểu dữ liệu của properties thành string
mặc cho chúng ta có truyền vào cái gì đi nữa.
Vậy là chúng ta đã tìm hiểu một số advanced types của Typescript, Typescript còn cung cấp rất nhiều advanced types khác nữa, chúng ta có thể tìm hiểu thêm tại đây: https://www.typescriptlang.org/docs/handbook/2/types-from-types.html. Cảm ơn các bạn đã theo dõi.