Typescript: Advanced Types

3 min read

typescript

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à findfindOne. Ở 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 BookServiceAuthorService đều sẽ có 2 hàm giống như abstract class và có kiểu dữ liệu là BookAuthor 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ả UnionType

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.

Avatar photo

Leave a Reply

Your email address will not be published. Required fields are marked *