5. Interface
Cùng với Class, TypeScript đưa vào thêm khái niệm
Interface. Tương tự với các ngôn ngữ khác, interface trong typescript cũng được sử dụng như 1 công cụ để tạo sự ràng buộc, quy ước giữa các thành phần tương tác trong hệ thống. Quy ước này được thể hiện qua 1 câu nói:“If it looks like a duck, and quacks like a duck, it’s a duck.”
Nói cách khác, interface được dùng như 1 cái giao kèo, đảm bảo rằng 1 đối tượng nào đó sẽ có các thành phần / cấu trúc như đã quy ước.
6. Why do you need an Interface?
Code JavaScript, hẳn ta rất quen thuộc với bài toán: Bên phía client lấy dữ liệu dạng JSON về từ server, sau đó làm các thứ với dữ liệu này. Giả sử , ta cần viết 1 hàm nhận vào là 1 Post và đầu ra là in ra cái gì đó được sinh ra từ post này:
function summary(post: {content: string}) { // Return post content with max is the first 10 characters. return post.content.slice(0, 10); } let post = { content: 'This is a very long long long long post.' } summary(post);
Để ý rằng: Để viết được logic của hàm summary, trong params của
summary ta phải khai báo kiểu object sẽ được truyền vào: post: {content: string}. Lúc này, interface có thể được sử dụng để viết lại hàm trên ngắn gon hơn.interface Post { content: string; } function summary( post: Post ) { // Return post content with max is the first 10 characters. return post.content.slice(0, 10); }
Bằng cách sử dụng interface
Post, ta tạo 1 quy ước cho đầu vào của function summary. Interface Post ở đây cũng có thể tái sử dụng ở những chỗ khác trong chương trình.Mà khoan! Có gì đó sai sai …
7. Khác #2: Interface property
Từ ví dụ trên , ta có thể thấy: trong Typescript,
Interface có thể chứa cả property.In Java - An interface is different from a class in several ways, including: - You cannot instantiate an interface. - An interface does not contain any constructors. - All of the methods in an interface are abstract. - An interface cannot contain instance fields. The only fields that can appear in an interface must be declared both static and final.
interface Animal { public void eat(); public void travel(); }
Mặt khác, đối với TypeScript:
One of TypeScript’s core principles is that type-checking focuses on values’ shape. This is sometimes called “duck typing” or “structural subtyping”. In TypeScript, interfaces fill the role of naming these types and are a powerful way of defining contracts within your code as well as contracts with code outside of your project.`
Interface lúc này đóng vai trò tạo nên ràng buộc cho các class implement lại nó, không chỉ là về các phương thức , mà còn cả về hình dạng (shape) của đối tượng được implement.8. Optional property
Property trong interface có thể không bắt buộc.
interface CarConfig { name?: string; year?: numeric; } function createCar(config: CarConfig) { let newCar = { name: 'Toyota', year: 1984 } if (config.name) { newCar = config.name } if (config.year) { newCar = config.year } return newCar; } let newCar = createCar({ name: 'Suzuki' });
Optional property trong interface được đánh dấu bởi dấu
?. Ưu điểm của optional property là nó cho phép ta khai báo các property có thể xuất hiện, đồng thời ngăn việc sử dụng các property mà chắc chắn không thuộc trong interface. Một ví dụ điển hình là việc lập trình viên có thể gõ sai chính tả tên 1 property nào đó.interface CarConfi g{ name?: string; year?: numeric; } function createCar(config: CarConfig) { let newCar = { name: 'Toyota', year: 1984 } if (config.name) { newCar = config.nane } // Error: Property 'nane' does not exist on type 'CarConfig' if (config.year) { newCar = config.year } return newCar; } let newCar = createCar({ name: 'Suzuki' });
9. Function type Interface
Ngoài property, interface cũng có thể mô tả object thông qua việc khai báo các function.
Khai báo function trong interface chỉ là đưa ra chữ kí (signature) của function đó, bao gồm:
- Danh sách các params
- Kiểu trả về của function.
interface IRun { (speed: numeric, destination: string): numeric; } let runner: IRun; runner = function(speed: numeric, destination: string): numeric { // ... implement function content }
10. Class type Interface
Use Case phổ biến nhất của interface đó là buộc một class phải tuân theo 1 quy ước khi implement.
interface IRun { startTime: Date; run(speed: numeric, destination: string): numeric; } class Runner implements IRun { startTime: Date; run(speed: numeric, destination: string): numeric { // return a numeric here! } }
11. Interface extend Interface
Cũng giống class, các interface có thể extend nhau. Một interface có thể extend cùng lúc nhiều interface khác.
interface Shape { cornerNumber: string; } interface Color { borderColor: string; } interface Square extends Shape, Color { sideLength: number; } let square = <Square>{}; square.cornerNumber = 4; square.borderColor = "blue"; square.sideLength = 10;
12. Khác #3: Interface extends Class
Không chỉ extends được interface khác, interface trong TypeScript cũng có khả năng extend được class!! Khi extend 1 class, interface sẽ kế thừa các member (Property, Function) của class đó, không ko phải implementation của chúng.
Chú ý: Các property được kế thừa sẽ bao gồm cả các property private và protected. Lúc này, interface chỉ có thể implement được bởi chính class đó hoặc các class con của nó.
class Parent { private privateProperty; } interface ISomething extends Parent { doSomething(): void; } class FirstChild implements ISomething { // Error: // Class 'FirstChild' incorrectly implements interface 'ISomething'. // Property 'privateProperty' is missing in type 'FirstChild' doSomething() { // Do something } } // Vẫn lỗi class SecondChild extends ISomething { // Class 'SecondChild' incorrectly implements interface 'ISomething'. // Property 'privateProperty' is private in type 'ISomething' but not in type 'SecondChild' privateProperty; doSomething() { // Do something } } // Ngay cả khai báo thế này cũng không ăn thua :v class ThirdChild extends ISomething { // Class 'ThirdChild' incorrectly extends baseclass 'Parent'. // Types have seperate declarations of a private property 'privateProperty' private privateProperty; doSomething() { // Do something } } // Ta bắt buộc phải khai báo thế này để sử dụng đc interface class ForthChild extends Parent implements ISomething { domeSomething() { // Do something } }
13. Khác #4: Type compatibility
Đọc đoạn code dưới đây, lập trình viên C#, Java sẽ thấy ngay có gì đó sai sai:
interface Named { name: string; } class Human { name: string; } let boy: Named; boy = new Human();
Chuyện gì xảy ra vậy !? class
Human và interface Named chả có liên quan gì tới nhau cả ??Trên thực tế, cấu trúc của TypeScript được xây dựng dựa trên cách JavaScript thường được viết. Trong JavaScript, các anonymous object được sử dụng rất rộng rãi, điển hình là việc sử dụng các JSON object được lấy về dưới dạng kết quả của API call. Vì thế nó dẫn tới quy ước sau được sử dụng trongTypeScript.
xđược coi là compatible vớiynếuychứa (ít nhất) đầy đủ các member củax.
Ví dụ:
interface Named { name: string; } let x: Named; let y = { name: 'Alice', location: 'Seattle' }; x = y; // OK, bởi vì y chứa property name của interface Named
Để kiểm tra xem
y có thể assign được cho x không, compiler sẽ kiểu tra từng property của x để tìm property tương ứng trong y. Như trong ví dụ trên, y ít nhất phải có 1 member name dạng string.Chú ý rằng
y có thêm property location nữa, nhưng điều này là được cho phép và sẽ không bị compile báo lỗi.