TypeScript là một ngôn ngữ lập trình mở rộng của JavaScript, giúp mã nguồn dễ đọc và hạn chế lỗi nhờ cơ chế kiểm tra kiểu tĩnh. Là ngôn ngữ được phát triển bởi Microsoft, TypeScript ngày càng phổ biến trong việc xây dựng các ứng dụng web hiện đại. Vậy TypeScript là gì và hiệu quả hoạt động của chúng như thế nào?
Đọc bài viết sau đây để hiểu rõ hơn về:
- Tổng quan về những điểm mạnh và công dụng nổi bật của TypeScript là gì
- Hệ thống gõ của TypeScript
- TypeScript Object-Oriented Programming (OOP)
- Sự khác biệt giữa TypeScript vs JavaScript
TypeScript là gì?
TypeScript là một ngôn ngữ lập trình mã nguồn mở được phát triển và duy trì bởi Microsoft từ năm 2012. TypeScript được xem là một phần mở rộng của JavaScript, sử dụng cú pháp của JavaScript và bổ sung thêm một số tính năng như kiểu tĩnh, class, hướng đối tượng,…
Mã TypeScript không được trình duyệt diễn giải trực tiếp để hiển thị trên web nên cần phải biên dịch mã TypeScript thành mã JavaScript thuần túy. Chính vì vậy, khi lập trình TypeScript sẽ cần đến Trình biên dịch TypeScript (tsc) để chuyển đổi mã sang JavaScript dễ dàng hơn.
Ví dụ:
Dưới đây là đoạn code HTML có gọi và sử dụng file types.js:
<!DOCTYPE html> <html> <body> <h2>ITviec</h2> <p>ITviec Blog - Ý tưởng phát triển sự nghiệp IT của bạn</p> <script src="types.js"></script> </body> </html>
Tạo file types.ts và thêm đoạn code sau:
let myString: string; myString = 'Hello from ts'; console.log(myString);
Sau khi có file types.ts và file HTML trên, bạn sẽ cần dịch sang mã TypeScript. Thực hiện nhập lệnh như sau:
tsc types.ts
Khi biên dịch thành công, sẽ tạo được một file JavaScript cùng tên và phần mở rộng .js, tức là type.js chứa mã đã biên dịch trong cùng thư mục. Bây giờ khi chạy file HTML sẽ hiển thị kết quả là mã JavaScript chuẩn.
Điểm nổi bật của TypeScript là gì?
Kiểu tĩnh
Kiểu tĩnh của TypeScript cho phép bạn kiểm tra và gán kiểu cho các biến, tham số và giá trị trả về của hàm. Đồng thời, kiểu tĩnh tùy chọn cũng giúp ngăn ngừa lỗi và cải thiện mã dễ đọc hơn.
Đối tượng dựa trên lớp (Class-based Object)
Một trong những tính năng nổi bật của TypeScript là hỗ trợ cho các lớp. Không giống như cách tiếp cận dựa trên nguyên mẫu của JavaScript, TypeScript cho phép bạn viết mã hướng đối tượng. Bạn có thể tạo lớp (class), định nghĩa hàm và sử dụng các trình sửa đổi truy cập (public, private, protected).
Mô-đun
TypeScript sử dụng các mô-đun để sắp xếp mã thành các phần nhỏ hơn và có thể tái sử dụng. Khả năng mô-đun này của TypeScript giúp nâng cao khả năng bảo trì và sự phối hợp giữa các thành viên trong tính chất dự án lớn.
Tính năng ES6
Là một ngôn ngữ mở rộng của JavaScript nên TypeScript cũng sử dụng các tính năng của ECMAScript 6 (hay còn gọi là ES6). Nếu bạn đã sử dụng quen thuộc với cú pháp của ES6 như hàm mũi tên, ký tự mẫu,… thì bạn sẽ cảm thấy thoải mái và không quá khó khăn khi bắt đầu sử dụng TypeScript.
Ưu và nhược điểm của TypeScript là gì?
Là một ngôn ngữ mở rộng của JavaScript nên TypeScript cũng sở hữu các ưu điểm chính của JavaScript. Đồng thời cung cấp các lợi ích bổ sung đến từ kiểu tĩnh và khái niệm khác dành riêng cho TypeScript.
- Sửa lỗi nhanh: TypeScript có thể phát hiện các lỗi ở giai đoạn biên dịch giúp tiết kiệm thời gian cho lập trình viên và cho phép họ tập trung vào việc sửa chữa các lỗi trong logic.
- Khả năng dự đoán: Nếu một biến được khai báo dưới dạng một chuỗi, TypeScript sẽ luôn là một chuỗi và sẽ không biến thành Boolean. Điều này làm tăng khả năng các chức năng hoạt động theo cách dự định ban đầu.
- Tái cấu trúc mã: TypeScript giúp quá trình tái cấu trúc hoặc cập nhật ứng dụng hoạt động trơn tru mà vẫn giữ được cơ sở mã ban đầu. Bên cạnh đó, TypeScript cũng có thể phát hiện lỗi tự động, giúp đơn giản hoá và tăng tốc tái cấu trúc mã. Điều này sẽ đặc biệt có lợi nếu bạn xử lý phần lớn của cơ sở mã.
- Tương thích đa nền tảng: Mọi thiết bị, nền tảng hoặc trình duyệt tương thích JavaScript cũng hoạt động được với TypeScript, sau khi trình biên dịch chuyển đổi mã thành JavaScript.
- Hỗ trợ công cụ: Cung cấp các công cụ phát triển tuyệt vời như IntelliSense, gợi ý thực khi viết mã.
- IDE: Nâng cao trải nghiệm của lập trình viên với mô-đun trình chỉnh sửa vượt trội.
- Tài liệu API: Đảm bảo tài liệu tốt hơn cho các API đồng bộ với mã nguồn, có khả năng giảm lỗi.
Bên cạnh đó, một số điểm yếu của TypeScript là gì?
- Một số lập trình viên thành thạo C#, C++ hoặc Java cho rằng TypeScript không phải là một ngôn ngữ đánh máy tĩnh thực sự.
- Mất nhiều thời gian biến dịch mã.
- Yêu cầu file định nghĩa cho thư viện của bên thứ ba, có thể không phải lúc nào cũng có sẵn hoặc có chất lượng tốt.
- Cần có bước biên dịch để chuyển đổi từ TypeScript sang JavaScript.
Hệ thống gõ của TypeScript
Hệ thống gõ của TypeScript được thiết kế cho phép thể hiện các kiểu (type) trong TypeScript theo các kiểu khác nhau. Generics được xem là kiểu cơ bản của hệ thống gõ trong TypeScript. Bên cạnh đó, cũng có nhiều loại kiểu khác nhau để sử dụng.
Bằng cách kết hợp nhiều loại kiểu khác nhau, bạn có thể thực hiện các phép toán và giá trị phức tạp theo cách ngắn gọn cũng như dễ bảo trì hơn.
Kiểu cơ bản
Chú thích cơ bản (Basic Annotations)
Kiểu chú thích cơ bản được thiết kế để bất kỳ thứ gì có sẵn trong không gian khai báo kiểu đều có thể được sử dụng để làm chú thích. Cú pháp của của chú thích cơ bản:
:TypeAnnotation
Ví dụ sau sẽ minh họa kiểu chú thích cho các biến, tham số hàm và giá trị trả về của hàm như sau:
var num: number = 123; function identity(num: number): number { return num; }
Kiểu nguyên thủy (Primitive Types)
Các kiểu nguyên thủy của JavaScript được thể hiện rõ trong hệ thống kiểu của TypeScript. Kiểu nguyên thủy cung cấp các khối xây dựng cơ bản như chuỗi, số, boolean,.. đại diện cho các kiểu dữ liệu cơ bản được sử dụng trong toàn bộ mã.
var num: number; var str: string; var bool: boolean; num = 123; num = 123.456; num = '123'; // Lỗi str = '123'; str = 123; // Lỗi bool = true; bool = false; bool = 'false'; // Lỗi
Mảng (Arrays)
TypeScript cung cấp cú pháp chuyên dụng cho mảng để lập trình viên dễ dàng chú thích và ghi chú mã của mình. Cú pháp của mảng sẽ thêm hậu tố [] vào bất kỳ chú thích kiểu hợp lệ nào (ví dụ: boolean[]). Nó sẽ cho phép bạn thực hiện các thao tác mảng mà bạn thường làm một cách an toàn, hạn chế các lỗi như gán không đúng kiểu.
var boolArray: boolean[]; boolArray = [true, false]; console.log(boolArray[0]); // true console.log(boolArray.length); // 2 boolArray[1] = true; boolArray = [false, false]; boolArray[0] = 'false'; // Error! boolArray = 'false'; // Error! boolArray = [true, 'false']; // Error!
Giao diện (Interfaces)
Interface là hệ thống gõ cốt lõi trong TypeScript được dùng để biên soạn nhiều kiểu chú thích thành một tên duy nhất. Ví dụ:
interface Name { first: string; second: string; } var name: Name; name = { first: 'John', second: 'Doe' }; name = { //Lỗi: `second` không xác định được first: 'John' }; name = { // Error : `second` sai kiểu first: 'John', second: 1337 };
Ở ví dụ trên, biên soạn các chú thích như string + second:string thành một chú thích mới với Name thực thi kiểm tra kiểu trên từng đoạn mã.
Nội tuyến (Inline Type Annotation)
Thay vì tạo một giao diện (interface) mới, bạn có thể chú thích bất kỳ thứ gì bạn muốn bằng cách sử dụng :{ /*Structure*/ }.
Kiểu nội tuyến nhanh chóng cung cấp chú thích kiểu một lần cho một thứ gì đó, giúp bạn tiết kiệm công sức nghĩ ra tên kiểu. Tuy nhiên, nếu bạn thấy mình đưa cùng một chú thích kiểu nội tuyến nhiều lần thì bạn nên cân nhắc việc tái cấu trúc nó thành interface.
var name: { first: string; second: string; }; name = { first: 'John', second: 'Doe' }; name = { first: 'John' }; name = { first: 'John', second: 1337 };
Generics
Viết mã một cách linh hoạt và có thể hoạt động với các kiểu dữ liệu khác nhau bằng cách sử dụng generic. Điều này làm giảm sự trùng lặp mã và thúc đẩy khả năng bảo trì tốt hơn.
interface Lengthwise { length: number; } function loggingIdentity<T extends Lengthwise>(arg: T): T { console.log(arg.length); return arg; }
Các kiểu đặc biệt
any
any giữ một vị trí đặc biệt trong hệ thống kiểu TypeScript, giúp cung cấp một lối thoát khỏi hệ thống kiểu để báo cho trình biên dịch. any tương thích với tất cả các kiểu trong hệ thống kiểu. Điều này có nghĩa là bất kỳ kiểu nào cũng có thể được gán cho any và nó có thể được gán cho bất kỳ kiểu nào.
Ví dụ:
var power: any; // Có thể lấy bất kỳ hoặc tất cả các kiểu power = '123'; power = 123; // Tương thích với tất cả các kiểu var num: number; power = num; num = power;
null and undefined
null và undefined được xử lý bởi hệ thống kiểu phụ thuộc vào strictNullChecks. Khi ở trong strictNullCheck:false, các ký tự JavaScript null và undefined được xử lý hiệu quả bởi hệ thống kiểu giống như any. Các ký tự này cũng có thể được gán cho bất kỳ kiểu nào khác.
Ví dụ như:
var num: number; var str: string; num = null; str = undefined;
void
Sử dụng :void để biểu thị rằng một hàm không có kiểu trả về:
function log(message): void { console.log(message); }
Kiểu nâng cao
Union Type
Union type là kiểu khá phổ biến trong JavaScript cho phép một thuộc tính trong nhiều kiểu, ví dụ như chuỗi (string) hoặc số (number). Một trường hợp sử dụng union type phổ biến là một hàm có thể lấy một đối tượng duy nhất hoặc một mảng của đối tượng, ví dụ:
function formatCommandline(command: string[]|string) { if (typeof command === 'string') { return command.trim(); } else if (Array.isArray(command)) { return command.join(' ').trim(); } throw new Error('Invalid command type'); }
Intersection Type
extend là một kiểu phổ biến trong JavaScript, trong đó lấy hai đối tượng và tạo một đối tượng mới có đầy đủ tính năng của cả hai đối tượng này. Intersection Type cho phép bạn sử dụng mẫu này theo cách an toàn hơn, ví dụ như:
function extend<T, U>(first: T, second: U): T & U { return { ...first, ...second }; } const x = extend({ a: "hello" }, { b: 42 }); // x có cả giá trị `a` và `b` const a = x.a; const b = x.b;
Tuple Type
JavaScript không có hỗ trợ tuple type, lập trình viên thường chỉ sử dụng một mảng như một tuple. Tuple có thể được chú thích bằng cách sử dụng [typeofmember1, typeofmember2]. Một tuple có thể có bất kỳ số lượng giá trị.
Ví dụ:
var nameNumber: [string, number]; // Okay nameNumber = ['Jenny', 8675309]; // Lỗi! nameNumber = ['Jenny', '867-5309'];
Kết hợp với sự hỗ trợ phân hóa trong TypeScript, các bộ dữ liệu của type tuple trông khá tốt nhưng thực chất chúng là các mảng (array) khác nhau.
var nameNumber: [string, number]; nameNumber = ['Jenny', 8675309]; var [name, num] = nameNumber;
Type Alis
Kiểu biệt danh (Type aliases) cho phép tạo tên tùy chỉnh (biệt danh) cho các kiểu TypeScript hiện có, bao gồm các kiểu nguyên thủy, kiểu hợp nhất, kiểu giao nhau và thậm chí các kiểu phức tạp hơn như kiểu đối tượng và kiểu hàm. Biệt danh kiểu được định nghĩa bằng từ khóa kiểu theo sau là tên mới, như được hiển thị bên dưới:
type Result = "success" | "error"; type Point = { x: number; y: number }; type Greeting = (name: string) => string; const status: Result = "success"; const origin: Point = { x: 0, y: 0 }; const greet: Greeting = (name) => `Hello, ${name}!`;
Đoạn mã trên cho thấy các trường hợp sử dụng khác nhau của biệt danh cho các kiểu khác trong TypeScript và cách sử dụng chúng sau khi khai báo.
Không giống như giao diện (interfaces), bạn có thể cung cấp một type alis cho bất kỳ chú thích kiểu nào, miễn là chúng hữu ích cho những thứ như union type hoặc intersection type. Ví dụ:
type Text = string | { text: string }; type Coordinates = [number, number]; type Callback = (data: string) => void;
Lập trình hướng đối tượng (Object-Oriented Programming – OOP) trong TypeScript
OOP (Object-Oriented Programming) là một mô hình dựa trên khái niệm “đối tượng” (object) tương tác để tạo ra mã có thể bảo trì và tái sử dụng.
TypeScript classes
Các lớp (classes) là các mẫu hoặc bản thiết kế để tạo các đối tượng, tức là chúng định nghĩa dữ liệu (thuộc tính) và phương thức (hàm).
Các Access Modifiers trong TypeScript:
- public: Truy cập từ bất kỳ đâu (mặc định)
- private: Chỉ truy cập trong class
- protected: Truy cập trong class và class kế thừa
Ví dụ:
class Organization { private name: string; private yearFounded: number; constructor(name: string, yearFounded: number) { this.name = name; this.yearFounded = yearFounded; } public getDetails(): string { return `${this.name} was founded in ${this.yearFounded}.`; } } let organization = new Organization("ITviec", 2015); console.log(organization.name); // Lỗi: Thuộc tính 'name' là riêng tư và chỉ có thể truy cập được trong lớp 'Organization'. console.log(organization.getDetails()); // Output: ITviec được thành lập năm 2015.
Qua ví dụ trên, tạo một lớp Organization với các thuộc tính riêng là name và yearFounded, chỉ có thể được truy cập và sửa đổi trong lớp “Organization”.
Inheritance (Kế thừa)
class Animal { protected name: string; constructor(name: string) { this.name = name; } } class Dog extends Animal { bark() { return `${this.name} barks`; } }
Abstract Classes
abstract class Base { abstract getName(): string; printName() { console.log(this.getName()); } }
TypeScript Interface
Giao diện (interface) mô tả hình dạng của các đối tượng bằng cách liệt kê các thuộc tính và phương thức mà chúng cần, mà không cung cấp bất kỳ chi tiết triển khai nào.
interface FormData { firstName: string; lastName: string; email: string; age: number; } let formData: FormData = { firstName: "John", lastName: "Doe", email: "john.doe@example.com", age: 30 };
Ở ví dụ trên, định nghĩa một giao diện FormData biểu diễn cấu trúc của dữ liệu mẫu. Sau đó tạo ra một đối tượng formData với các thuộc tính tương ứng với định nghĩa giao diện.
Mối quan hệ giữa TypeScript vs JavaScript
Biên dịch sang JavaScript
Trình duyệt chỉ hiểu và thực thi mã JavaScript, do đó để chạy được mã TypeScript thì trước tiên mã phải được biên dịch hoặc chuyển đổi thành JavaScript. Quá trình biên dịch sẽ sử dụng TypeScript tsc, đây là trình biên dịch sẽ chuyển đổi mọi tính năng riêng cho TypeScript thành mã tương thích với công cụ JavaScript.
Quá trình biên dịch sẽ được bắt đầu bằng phân tích cú pháp rồi sau đó tiến hành dịch mã, cụ thể:
- Phân tích cú pháp: Trình biên dịch sẽ đọc mã TypeScript và phân tích hoặc chuyển mã thành cấu trúc dữ liệu.
- Kiểm tra kiểu: Trình biên dịch cũng sẽ thực hiện kiểm tra kiểu dựa trên chú thích (annotations) và phân tích để đảm bảo mã tuân thủ các kiểu đã xác định.
- Chuyển đổi: Thực hiện chuyển đổi mã dựa trên AST và kết quả kiểm tra kiểu.
- Tạo mã: Tạo mã JavaScript tương đương dựa trên cấu trúc AST đã chuyển đổi và giữ nguyên chức năng của mã TypeScript.
Khả năng tương thích với JavaScript
Cú pháp của JavaScript và TypeScript có thể cùng sử dụng trong một dự án. Tuy nhiên, để sử dụng khả năng của TypeScript cần có trình biên dịch mã sang JavaScript.
Khả năng tương thích giữa 2 ngôn ngữ lập trình này có thể đem lại một số lợi thế như:
- Cơ sở mã JavaScript có thể được di chuyển dần sang TypeScript, yêu cầu trình biên dịch TypeScript và tệp tsconfig chỉ định các tùy chọn trình biên dịch.
- TypeScript cung cấp hệ thống kiểu cho phép lập trình viên chỉ định kiểu biến.
- Mã TypeScript được biên dịch sang JavaScript trước khi thực thi nên sẽ tương thích với bất kỳ môi trường thời gian chạy của JavaScript.
So sánh TypeScript vs JavaScript
Bên cạnh đó, giữa TypeScript vs JavaScript còn có sự khác biệt qua một số tiêu chí như sau:
Tiêu chí | TypeScript | JavaScript |
Phát triển bởi | Microsoft | Brendan Eich (Netscape), hiện được quản lý bởi ECMAScript committee |
Kiểu | Static typing với optional type annotations | Dynamic typing và weak typing |
Công cụ | Đi kèm IDE và trình soạn thảo mã | Nhiều công cụ phát triển mạnh mẽ (DevTools, debugging tools, linters) |
Data Binding | Sử dụng interface hoặc type để xác định dữ liệu | Không có khái niệm nào được sử dụng |
Cú pháp | Tương tự JavaScript và có bổ sung các tính năng mới | Cú pháp JavaScript |
IDE Support | Hỗ trợ tái cấu trúc, tự động hoàn thành và kiểm tra type. | IDE khác nhau, tập trung vào highlight và tự động hoàn thành cú pháp cơ bản |
Debug | Mạnh hơn | Cần gỡ lỗi và thử nghiệm nhiều hơn |
Ứng dụng | Ứng dụng cho các dự án quy mô lớn, dự án theo nhóm hoặc dự án lâu dài | Đa dạng ứng dụng, đặc biệt là các dự án nhỏ |
Learning Curve | Cần có kiến thức về script và tìm hiểu thêm tính năng bổ sung của JavaScript | Cú pháp JavaScript dễ học, và ngôn ngữ linh hoạt để viết tập lệnh website |
Xem thêm: TypeScript vs JavaScript: Tính năng và trường hợp sử dụng
Câu hỏi thường gặp về TypeScript là gì
Học TypeScript mất bao lâu?
Thời gian học TypeScript sẽ phụ thuộc vào kinh nghiệm cũng như thời gian bạn có thể dành ra cho việc học. Nếu bạn là người mới bắt đầu thì bạn sẽ cần học trước về JavaScript, quá trình có thể mất vài tuần đến vài tháng. Hoặc nếu bạn đã có nền tảng kiến thức về JavaScript thì thời gian học và làm quen với TypeScript có thể rút ngắn hơn, trung bình 2-4 tuần.
Để việc học đạt hiệu quả, bạn cần xác định và lập cho mình một lộ trình học TypeScript phù hợp. Từ đó bạn sẽ có góc nhìn rõ hơn về thời gian học cũng như các kỹ năng mà bản thân sẽ đạt được sau khi học TypeScript.
Sử dụng TypeScript với React được không?
Việc sử dụng React để phát triển web là một lựa chọn phù hợp để xây dựng sự trực quan và phản hồi trên web với giao diện người dùng. Kết hợp React với TypeScript có thể đem lại một số lợi ích như:
- Dễ dàng phát hiện lỗi, làm việc với kiểu tĩnh và bảo trì mã dễ dàng hơn.
- Đi kèm với hỗ trợ và công cụ mạnh mẽ, có thể quản lý điều hướng mã, từ đó cải thiện hiệu suất dễ dàng.
- Cải thiện giao diện người dùng nhanh và hiệu quả hơn.
- Phát hiện lỗi compile-time.
- Type safety cho hooks và components.
Tổng kết TypeScript là gì
Được biết đến là ngôn ngữ mở rộng của JavaScript cùng cơ chế kiểu tĩnh, TypeScript giúp lập trình viên viết mã an toàn, dễ bảo trì và tối ưu hiệu suất. Với những lợi ích vượt trội so với JavaScript, TypeScript đang trở thành xu hướng phổ biến trong phát triển phần mềm. Như vậy, bài viết đã giải đáp chi tiết về TypeScript là gì cũng như những lợi ích vượt trội của TypeScript.