Trong hướng dẫn này, bạn sẽ tìm hiểu về các kiểu dữ liệu đối tượng trong TypeScript và cách sử dụng chúng.
Kiểu object
trong TypeScript đại diện cho tất cả các giá trị không có trong kiểu nguyên thủy.
Sau đây là các kiểu dữ liệu nguyên thủy trong TypeScript:
number
bigint
string
boolean
null
undefined
symbol
Sau đây là cách khai báo biến chứa một đối tượng:
let employee: object;
employee = {
firstName: 'John',
lastName: 'Doe',
age: 25,
jobTitle: 'Web Developer'
};
console.log(employee);
Đầu ra:
{
firstName: 'John',
lastName: 'Doe',
age: 25,
jobTitle: 'Web Developer'
}
Nếu bạn gán lại một giá trị kiểu nguyên thủy cho đối tượng employee
, bạn sẽ gặp lỗi:
employee = "Jane";
Lỗi:
error TS2322: Type '"Jane"' is not assignable to type 'object'.
Đối tượng employee
có kiểu dữ liệu object
với một danh sách các thuộc tính cố định. Nếu bạn cố gắng truy cập một thuộc tính không tồn tại trên đối tượng employee
, bạn cũng sẽ gặp lỗi:
console.log(employee.hireDate);
Lỗi:
error TS2339: Property 'hireDate' does not exist on type 'object'.
Lưu ý rằng câu lệnh trên hoạt động hoàn toàn bình thường trong JavaScript và giá trị undefined
được trả về.
Để chỉ định rõ ràng các thuộc tính của đối tượng employee
, trước tiên bạn sử dụng cú pháp sau để khai báo đối tượng employee
:
let employee: {
firstName: string;
lastName: string;
age: number;
jobTitle: string;
};
Và sau đó bạn gán một đối tượng với các thuộc tính được mô tả cho đối tượng employee
như sau:
employee = {
firstName: 'John',
lastName: 'Doe',
age: 25,
jobTitle: 'Web Developer'
};
Hoặc bạn có thể kết hợp cả hai cú pháp trong cùng một câu lệnh như sau:
let employee: {
firstName: string;
lastName: string;
age: number;
jobTitle: string;
} = {
firstName: 'John',
lastName: 'Doe',
age: 25,
jobTitle: 'Web Developer'
};
TypeScript có một kiểu dữ liệu khác được gọi là Object
với chữ O được viết hoa. Điều quan trọng là phải hiểu sự khác biệt giữa chúng.
Kiểu object
đại diện cho tất cả các giá trị phi nguyên thủy trong khi kiểu Object
mô tả các chức năng của tất cả các đối tượng.
Ví dụ, kiểu Object
có các phương thức toString()
và valueOf()
có thể được truy cập bởi bất kỳ đối tượng nào.
TypeScript có một kiểu khác được gọi là kiểu empty được ký hiệu là {}
, khá giống với kiểu đối tượng.
Kiểu empty {}
mô tả một đối tượng không có thuộc tính của riêng nó. Nếu bạn cố gắng truy cập một thuộc tính trên đối tượng như vậy, TypeScript sẽ báo lỗi khi biên dịch:
let vacant: {};
vacant.firstName = 'John';
Lỗi:
error TS2339: Property 'firstName' does not exist on type '{}'.
Nhưng bạn có thể truy cập tất cả các thuộc tính và phương thức được khai báo trên kiểu Object
, có sẵn trên đối tượng thông qua prototype:
let vacant: {} = {};
console.log(vacant.toString());
Đầu ra:
[object Object]
Kiểu array
trong TypeScript là một danh sách dữ liệu có thứ tự. Để khai báo một mảng chứa các giá trị của một kiểu dữ liệu cụ thể, bạn sử dụng cú pháp sau:
let arrayName: type[];
Ví dụ, phần sau khai báo một mảng các giá trị kiểu chuỗi:
let skills: string[];
Và bạn có thể thêm một hoặc nhiều chuỗi vào mảng như sau:
skills[0] = "Problem Solving";
skills[1] = "Programming";
Hoặc sử dụng phương thức push()
như sau:
skills.push('Software Design');
Phần sau khai báo một biến và gán một mảng chuỗi cho nó:
let skills = ['Problem Sovling','Software Design','Programming'];
Trong ví dụ này, TypeScript suy ra kiểu dữ liệu của biến skills
là một mảng kiểu chuỗi. Nó tương đương như sau:
let skills: string[];
skills = ['Problem Sovling','Software Design','Programming'];
Khi bạn định nghĩa một mảng có kiểu dữ liệu cụ thể, TypeScript sẽ ngăn bạn thêm các giá trị không tương thích vào mảng.
Điều sau sẽ gây ra lỗi:
skills.push(100);
Bởi vì chúng ta đang cố gắng thêm một số vào mảng chuỗi.
Lỗi:
Argument of type 'number' is not assignable to parameter of type 'string'.
Khi bạn truy xuất một phần tử từ mảng, TypeScript có thể thực hiện suy luận kiểu dữ liệu. Ví dụ:
let skill = skills[0];
console.log(typeof(skill));
Đầu ra:
string
Trong ví dụ này, chúng tôi truy xuất phần tử đầu tiên của mảng skills
và gán nó cho biến skill
.
Vì một phần tử trong mảng chuỗi là một chuỗi, nên TypeScript suy ra kiểu của biến skill
là kiểu chuỗi như được hiển thị trong đầu ra.
Mảng trong TypeScript có thể truy cập các thuộc tính và phương thức của JavaScript. Ví dụ: phần sau sử dụng thuộc tính length
để lấy số phần tử trong một mảng:
let series = [1, 2, 3];
console.log(series.length); // 3
Và bạn có thể sử dụng tất cả các phương thức mảng hữu ích như forEach()
, map()
, reduce()
, và filter()
. Ví dụ:
let series = [1, 2, 3];
let doubleIt = series.map(e => e* 2);
console.log(doubleIt);
Đầu ra:
[ 2, 4, 6 ]
Phần sau minh họa cách khai báo một mảng chứa cả chuỗi và số:
let scores = ['Programming', 5, 'Software Design', 4];
Trong trường hợp này, TypeScript suy luận kiểu dữ liệu của biến scores
là một mảng kiểu string | number
.
Nó tương đương như sau:
let scores : (string | number)[];
scores = ['Programming', 5, 'Software Design', 4];
Tuple hoạt động giống như một mảng với một số bổ sung:
Ví dụ: bạn có thể sử dụng Tuple để mô tả một giá trị dưới dạng một cặp string
và number
:
let skill: [string, number];
skill = ['Programming', 5];
Thứ tự của các giá trị trong Tuple rất quan trọng. Nếu bạn thay đổi thứ tự các giá trị của Tuple skill
thành [5, "Programming"]
, bạn sẽ gặp lỗi:
let skill: [string, number];
skill = [5, 'Programming'];
Lỗi:
error TS2322: Type 'string' is not assignable to type 'number'.
Vì lý do này, bạn nên sử dụng các bộ dữ liệu có liên quan đến nhau theo một thứ tự cụ thể.
Ví dụ: bạn có thể sử dụng Tuple để định nghĩa màu RGB luôn có dạng ba số như sau:
(r, g, b)
Ví dụ:
let color: [number, number, number] = [255, 0, 0];
Các giá trị color[0]
, color[1]
và color[2]
sẽ được ánh xạ tới các giá trị màu Red
, Green
và Blue
.
Kể từ TypeScript 3.0, Tuple có thể có các phần tử tùy chọn được chỉ định bằng cách sử dụng hậu tố dấu hỏi (?).
Ví dụ: bạn có thể định nghĩa một bộ RGBA với giá trị kênh alpha tùy chọn như sau:
let bgColor, headerColor: [number, number, number, number?];
bgColor = [0, 255, 255, 0.5];
headerColor = [0, 255, 255];
Lưu ý rằng RGBA xác định màu sắc bằng cách sử dụng mô hình màu đỏ, xanh lá cây, xanh lam và alpha. Alpha xác định độ mờ của màu.
Enum là một nhóm các giá trị hằng số được đặt tên. Enum là viết tắt của kiểu liệt kê.
Để định nghĩa một enum, bạn làm theo các bước sau:
enum
theo sau là tên của enum.Sau đây là cú pháp để xác định một enum:
enum name {constant1, constant2, ...};
Trong cú pháp này, constant1
, constant2
, vv, cũng được gọi là các thành viên của enum.
Ví dụ sau tạo một enum đại diện cho các tháng trong năm:
enum Month {
Jan,
Feb,
Mar,
Apr,
May,
Jun,
Jul,
Aug,
Sep,
Oct,
Nov,
Dec
};
Trong ví dụ này, tên enum là Month
và các giá trị hằng số là Jan
, Feb
, Mar
, vv.
Phần sau khai báo một hàm sử dụng enum Month
làm kiểu dữ liệu cho tham số month
:
function isItSummer(month: Month) {
let isSummer: boolean;
switch (month) {
case Month.Jun:
case Month.Jul:
case Month.Aug:
isSummer = true;
break;
default:
isSummer = false;
break;
}
return isSummer;
}
Và bạn có thể thực thi nó như sau:
console.log(isItSummer(Month.Jun)); // true
Ví dụ này sử dụng các giá trị Jan
, Feb
, Mar
, ... trong enum thay cho các giá trị 1
, 2
, 3
, ... Điều này làm cho đoạn code rõ ràng hơn.
Một best practice là sử dụng các giá trị không đổi được xác định bởi các enum trong code.
Tuy nhiên, ví dụ sau truyền một số thay vì một enum vào hàm isItSummer()
. Và nó hoạt động.
console.log(isItSummer(6)); // true
Ví dụ này sử dụng một số (6
) thay vì một hằng số được định nghĩa bởi enum Month
. Và nó hoạt động.
Hãy kiểm tra mã Javascript được tạo của enum Month
:
var Month;
(function (Month) {
Month[Month["Jan"] = 0] = "Jan";
Month[Month["Feb"] = 1] = "Feb";
Month[Month["Mar"] = 2] = "Mar";
Month[Month["Apr"] = 3] = "Apr";
Month[Month["May"] = 4] = "May";
Month[Month["Jun"] = 5] = "Jun";
Month[Month["Jul"] = 6] = "Jul";
Month[Month["Aug"] = 7] = "Aug";
Month[Month["Sep"] = 8] = "Sep";
Month[Month["Oct"] = 9] = "Oct";
Month[Month["Nov"] = 10] = "Nov";
Month[Month["Dec"] = 11] = "Dec";
})(Month || (Month = {}));
Và bạn có thể xuất biến kiểu Month
ra console:
{
'0': 'Jan',
'1': 'Feb',
'2': 'Mar',
'3': 'Apr',
'4': 'May',
'5': 'Jun',
'6': 'Jul',
'7': 'Aug',
'8': 'Sep',
'9': 'Oct',
'10': 'Nov',
'11': 'Dec',
Jan: 0,
Feb: 1,
Mar: 2,
Apr: 3,
May: 4,
Jun: 5,
Jul: 6,
Aug: 7,
Sep: 8,
Oct: 9,
Nov: 10,
Dec: 11
}
Như bạn có thể thấy rõ ràng từ đầu ra, một enum TypeScript là một đối tượng trong JavaScript. Đối tượng này có các thuộc tính được đặt tên được khai báo trong enum. Ví dụ, Jan
là 0
và Feb
là 1
.
Đối tượng được tạo cũng có các khóa với các giá trị chuỗi số đại diện cho các hằng số được đặt tên.
Đó là lý do tại sao bạn có thể truyền một số vào hàm chấp nhận một enum. Nói cách khác, một thành viên enum vừa là một số vừa là một hằng số xác định.
TypeScript xác định giá trị của thành viên enum dựa trên thứ tự của thành viên đó xuất hiện trong định nghĩa enum (mặc định: thành viên đầu tiên có giá trị là 0). Ví dụ: trong enum Month
thì thành viên Jan
có giá trị 0, Feb
có giá trị 1, v.v.
Bạn có thể chỉ định rõ ràng các số cho các thành viên của một enum như sau:
enum Month {
Jan = 1,
Feb,
Mar,
Apr,
May,
Jun,
Jul,
Aug,
Sep,
Oct,
Nov,
Dec
};
Trong ví dụ này, giá trị của phần tử Jan
sẽ là 1 thay vì 0. Giá trị Feb
sẽ là 2 và giá trị của Mar
là 3, v.v.
Bạn nên sử dụng một enum khi bạn:
Ví dụ: bạn có thể sử dụng một enum cho trạng thái phê duyệt:
enum ApprovalStatus {
draft,
submitted,
approved,
rejected
};
Sau đó, bạn có thể sử dụng enum ApprovalStatus
như sau:
const request = {
id: 1,
status: ApprovalStatus.approved,
description: 'Please approve this request'
};
if(request.status === ApprovalStatus.approved) {
// send an email
console.log('Send email to the Applicant...');
}
Đôi khi, bạn có thể cần lưu trữ một giá trị trong một biến. Nhưng bạn không biết kiểu dữ liệu của nó tại thời điểm viết chương trình. Và giá trị không xác định có thể đến từ API của bên thứ ba hoặc đầu vào của người dùng.
Trong trường hợp này, bạn muốn chọn không kiểm tra kiểu dữ liệu và cho phép giá trị vượt qua kiểm tra lúc biên dịch.
Để làm như vậy, bạn sử dụng kiểu dữ liệu any
. Kiểu dữ liệu any
cho phép bạn gán một giá trị của bất kỳ kiểu dữ liệu nào cho một biến:
// json may come from a third-party API
const json = `{"latitude": 10.11, "longitude":12.12}`;
// parse JSON to find location
const currentLocation = JSON.parse(json);
console.log(currentLocation);
Đầu ra:
{ latitude: 10.11, longitude: 12.12 }
Trong ví dụ này, biến currentLocation
được gán cho một đối tượng do hàm JSON.parse()
trả về.
Tuy nhiên, khi bạn sử dụng biến currentLocation
để truy cập các thuộc tính, TypeScript cũng sẽ không thực hiện bất kỳ kiểm tra nào:
console.log(currentLocation.x);
Đầu ra:
undefined
Trình biên dịch TypeScript không phàn nàn hoặc đưa ra bất kỳ lỗi nào.
Kiểu dữ liệu any
cung cấp cho bạn một cách để làm việc với codebase JavaScript. Nó cho phép bạn chọn tham gia dần dần và chọn không kiểm tra kiểu dữ liệu trong quá trình biên dịch. Do đó, bạn có thể sử dụng kiểu any
để chuyển một dự án JavaScript sang TypeScript.
Nếu bạn khai báo một biến mà không chỉ định kiểu dữ liệu, TypeScript sẽ giả định rằng bạn sử dụng kiểu any
. Tính năng này được gọi là suy luận kiểu dữ liệu. Về cơ bản, TypeScript sẽ đoán kiểu dữ liệu của biến. Ví dụ:
let result;
Trong ví dụ này, TypeScript suy ra kiểu dữ liệu cho bạn. Thực hành này được gọi là kiểu dữ liệu không rõ ràng (implicit type).
Lưu ý rằng để tắt tính năng nhập ẩn choany
kiểu, bạn thay đổinoImplicitAny
tùy chọn trongtsconfig.json
tệp thành true. Bạn sẽ tìm hiểu thêm về điều nàytsconfig.json
trong hướng dẫn sau.
Nếu bạn khai báo một biến với kiểu object
, bạn cũng có thể gán cho nó bất kỳ giá trị nào.
Tuy nhiên, bạn không thể gọi một phương thức trên nó ngay cả khi phương thức đó thực sự tồn tại. Ví dụ:
let result: any;
result = 10.123;
console.log(result.toFixed());
result.willExist(); //
Trong ví dụ này, trình biên dịch TypeScript không đưa ra bất kỳ cảnh báo nào ngay cả khi phương thức willExist()
không tồn tại tại thời điểm biên dịch vì phương thức willExist()
có thể có sẵn trong lúc thực thi.
Tuy nhiên, nếu bạn thay đổi kiểu của biến result
thành object
, trình biên dịch TypeScript sẽ xuất hiện lỗi:
let result: object;
result = 10.123;
result.toFixed();
Lỗi:
error TS2339: Property 'toFixed' does not exist on type 'object'.
object
trong TypeScript đại diện cho bất kỳ giá trị nào không phải là giá trị nguyên thủy.Object
mô tả các chức năng có sẵn trên tất cả các đối tượng.{}
đề cập đến một đối tượng không có thuộc tính riêng của nó.let arr: type[]
.any
trong TypeScript cho phép bạn lưu trữ một giá trị thuộc bất kỳ kiểu nào. Nó hướng dẫn trình biên dịch bỏ qua việc kiểm tra kiểu dữ liệu.any
để lưu trữ một giá trị mà bạn thực sự không biết kiểu dữ liệu của nó tại thời điểm biên dịch hoặc khi bạn chuyển một dự án JavaScript sang một dự án TypeScript.Bạn có thể vui lòng tắt trình chặn quảng cáo ❤️ để hỗ trợ chúng tôi duy trì hoạt động của trang web.
Namespace được sử dụng để nhóm logic các chức năng. Namespace có thể bao gồm các interface, lớp, hàm và biến để hỗ trợ một nhóm các chức năng liên quan.
Trong hướng dẫn này bạn sẽ tìm hiểu về từ khóa readonly, static và cách sử dụng chúng trong TypeScript.
TypeScript có ba từ khóa kiểm soát quyền truy cập: public, private và protected để kiểm soát khả năng hiển thị của các thành phần dữ liệu của nó.
Định nghĩa một abstract class trong TypeScript bằng cách sử dụng từ khóa abstract. Abstract class chủ yếu được sử dụng để các lớp khác kế thừa từ chúng. Chúng ta không thể tạo một thể hiện của một abstract class.