- vừa được xem lúc

Tìm hiểu về một vài kiểu dữ liệu trong Typescript

0 0 38

Người đăng: Đặng Xuân Thắng

Theo Viblo Asia

Trong bài viết này chúng ta sẽ đi tìm hiểu về Union/Intersection/Conditional type và type guards.

1. Union type

Union Types là những type mang tính chất: EITHER OR (tạm dịch là Hoặc cái này Hoặc cái kia). Để viết Union Types, chúng ta dùng Pipe Symbol (|).

let text: string | string[];

Biến text có thể nhận kiểu string hoặc mảng string.
Ví dụ :

function print(text: string | string[]): string { if (typeof text === 'string') { return text; } // compiler now knows that you can use join // and that variable type is definitely string[] return text.join(' ');
} let x = print('hello text');
let y = print(['hello', 'text', 'array']); // let z = print(5); // Error: Argument of type '5' is not assignable to type 'string | string[]' console.log(x); //"hello text" console.log(y);//"hello text array" 

Chúng ta có thể sử dụng union type với class/interface

interface IStudent { id: string; age: number;
} interface IWorker { companyId: string;
} type IUnionType = IStudent | IWorker; let p: IUnionType = { id: 'ID3241', age: 21
}; // p = 3; // Type '3' is not assignable to type 'IUnionType' p = { companyId: 'cid993'
};

2. Intersection type

Kiểu giao (Intersection Type) trong TypeScript tạo ra một kiểu dữ liệu mới bằng cách kết hợp hai hay nhiều kiểu dữ liệu lại với nhau. Kiểu dữ liệu mới có tất cả các thuộc tính của các kiểu dữ liệu hiện có.
Ví dụ : Ta có các interface

interface BusinessPartner { name: string; credit: number;
} interface Identity { id: number; name: string;
} interface Contact { email: string; phone: string;
}

Ta định nghĩa các kiểu giao:

type Employee = Identity & Contact;
type Customer = BusinessPartner & Contact;

Kiểu Employee có chứa tất cả các thuộc tính của kiểu dentityContact:

type Employee = Identity & Contact; let e: Employee = { id: 100, name: 'John Doe', email: '_@.com', phone: '(408)-897-5684'
};

Và kiểu Customer chứa tất cả các thuộc tính của kiểu BusinessPartnerContact:

type Customer = BusinessPartner & Contact; let c: Customer = { name: 'ABC Inc.', credit: 1000000, email: '_@.com', phone: '(408)-897-5735'
};

Ta cũng có thể tạo ra một kiểu giao mới có chứa tất cả các thuộc tính của kiểu dữ liệu Identity,ContactBusinessPartner như sau:

type Employee = Identity & BusinessPartner & Contact; let e: Employee = { id: 100, name: 'John Doe', email: '_@.com', phone: '(408)-897-5684', credit: 1000
};
  • Hãy cẩn thận khi kết hợp cáctype/interface có các thuộc tính cùng tên nhưng không cùng kiểu dữ liệu
interface X { c: string; d: string;
} interface Y { c: number; e: string
} type XY = X & Y;
type YX = Y & X; let p: XY;
let q: XY; p.c = 4; // Error: Type '4' is not assignable to type 'string & number'
q.c = 3; // Error: Type '3' is not assignable to type 'string & number' p.c = 'text'; // Error: Type 'text' is not assignable to type 'string & number'
q.c = 'text'; // Error: Type 'text' is not assignable to type 'string & number'
  • Bạn sẽ nhận thấy rằng kiểu X và kiểu Y đều có thuộc tính c. Tuy nhiên, loại thuộc tính không giống nhau và khi ta cố gắng gán giá trị cho nó, ta sẽ gặp lỗi từ trình biên dịch.
  • Để kết hơp các interface có cùng tên thuộc tính thì kiểu dữ liệu của thuộc tính đó không phải là kiểu nguyên thủy :
interface D { d: boolean; }
interface E { e: string; }
interface F { f: number; } interface A { x: D; }
interface B { x: E; }
interface C { x: F; } type ABC = A & B & C; let abc: ABC = { x: { d: true, e: 'codingblast', f: 3 }
}; console.log('abc:', abc);

3. Type guards

An toàn kiểu (type guard) trong TypeScript cho phép bạn thu hẹp kiểu dữ liệu của biến trong khối lệnh điều kiện.

3.1. Toán tử typeof trong TypeScript

Ví dụ :

type alphanumeric = string | number; function add(a: alphanumeric, b: alphanumeric) { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' && typeof b === 'string') { return a.concat(b); } throw new Error('Invalid arguments. Both arguments must be either numbers or strings.');
}

Toán tử typeof hoạt động như thế nào:

  • Đầu tiên, định nghĩa kiểu alphanumeric là kiểu kết hợp có thể chứa một chuỗi hoặc một số .
  • Tiếp theo, khai báo một hàm có thêm hai biếnab với kiểu alphanumeric.
  • Sau đó, kiểm tra xem kiểu dữ liệu của cả hai đối số có phải là số hay không bằng cách sử dụng toán tửtypeof. Nếu có, trả về tổng các đối số.
  • Ngược lại, kiểm tra xem kiểu dữ liệu của cả hai đối số có phải là chuỗi hay không bằng cách sử dụng toán tử typeof. Nếu có, trả về chuỗi được nối từ hai đối số.
  • Cuối cùng, đưa ra một lỗi nếu các đối số không phải là số hoặc chuỗi

Trong ví dụ này, TypeScript biết cách sử dụng toán tửtypeof trong các khối điều kiện. Bên trong khối if sau, TypeScript nhận ra rằng a và b là những con số.

if (typeof a === 'number' && typeof b === 'number') { return a + b;
}

Tương tự, trong khối if sau , TypeScript xử lý a và b dưới dạng chuỗi, do đó bạn có thể nối chúng thành một:

if (typeof a === 'string' && typeof b === 'string') { return a.concat(b);
}

3.2 Toán tử instanceof trong TypeScript

Ví dụ:

class Customer { isCreditAllowed(): boolean { // ... return true; }
} class Supplier { isInShortList(): boolean { // ... return true; }
} type BusinessPartner = Customer | Supplier; function signContract(partner: BusinessPartner) : string { let message: string; if (partner instanceof Customer) { message = partner.isCreditAllowed() ? 'Sign a new contract with the customer' : 'Credit issue'; } if (partner instanceof Supplier) { message = partner.isInShortList() ? 'Sign a new contract the supplier' : 'Need to evaluate further'; } return message;
}

Toán tử instanceof hoạt động như thế nào:

  • Đầu tiên, khai báo các lớp Customerupplier.
  • Thứ hai, tạo bí danh kiểuBusinessPartner là kiểu kết hợp củaCustomerSupplier.
  • Thứ ba, khai báo một hàm signContract() chấp nhận một tham số với kiểu BusinessPartner.
  • Cuối cùng, hãy kiểm tra xem đối số có phải là một thể hiện củaCustomer hoặcSupplier không và sau đó cung cấp logic tương ứng.

Bên trong khối if sau, TypeScript biết rằng partner là một thể hiện của kiểuCustomer do toán tử instanceof:

if (partner instanceof Customer) { message = partner.isCreditAllowed() ? 'Sign a new contract with the customer' : 'Credit issue';
}

Tương tự như vậy, TypeScript biết rằng partner là một thể hiện Supplier bên trong khối if sau :

if (partner instanceof Supplier) { message = partner.isInShortList() ? 'Sign a new contract with the supplier' : 'Need to evaluate further';
}

Khi câu lệnh if thu hẹp một kiểu dữ liệu, TypeScript biết rằng bên trong lệnh else không phải là kiểu dữ liệu đó mà là kiểu dữ liệu khác. Ví dụ:

function signContract(partner: BusinessPartner): string { let message: string; if (isCustomer(partner)) { message = partner.isCreditAllowed() ? 'Sign a new contract with the customer' : 'Credit issue'; } else { message = partner.isInShortList() ? 'Sign a new contract with the supplier' : 'Need to evaluate further'; } return message;
}

4.Conditional Types

Từ TypeScript version 2.8 thì TypeScript cung cấp cho developers Conditional Types. Đây là 1 type khá đặc biệt, giúp chúng ta tạo được những type dựa theo điều kiện. Ví dụ:

T extends U ? X : Y

Dòng code trên có thể hiểu là: khi T có thể gán được cho U thì sẽ trả về type X, còn không thì trả về type Y.

function fn<T extends boolean>(x: T): T extends true ? string : number { // do something
} const x = fn(true); // type của giá trị trả về của fn() sẽ có type là string | number

Conditional Types đơn giản chỉ có vậy, nhưng nếu biết cách sử dụng, mình có thể tạo được những types rất “robust” và phục vụ được việc develop rất nhiều.

Tài liệu tham khảo :

Bình luận