Lời mở đầu:
Khi đang tìm hiểu về decorators trong Typescipt, thì tôi bỗng bắt gặp 1 khái niệm
A Class Decorator is declared just before a class declaration. The class decorator is applied to the constructor of the class and can be used to observe, modify, or replace a class definition. A class decorator cannot be used in a declaration file, or in any other ambient context (such as on a
declare
class).>The expression for the class decorator will be called as a function at runtime, with the constructor of the decorated class as its only argument.
If the class decorator returns a value, it will replace the class declaration with the provided constructor function.
Đại khái là Class Decor apply trên "constructor of the class", được gọi lúc runtime và nhận "constructor of the class" là đối số duy nhất. Nếu Class Decor trả về 1 giá trị, nó sẽ thay thế Class với constructor function
mới
Điều làm tôi thắc mắc không hiểu "constructor of the class" nó là thứ gì? Nó có phải là method constructor
mà ta khai báo khi định nghĩa 1 class hay không? Kí hiệu về kiểu (type annotation) của nó là gì?
Bài viết sau là kết quả tôi đi tìm hiểu lời giải cho khái niệm này.
Class và Function trong Javascript
Đầu tiên thì Class
trong Javascript thực chất là 1 Function
. Function
thực chất lại là 1 Object
In fact, mọi đối tượng trong JS đều bắt nguồn từ đối tượng gốc
Object
. Loại đối tượng duy nhất không bắt nguồn từObject
, đó là bọn cóprototype
lànull
(kiểu ngang hàng với Object )
Vậy điểm khác nhau giữa Class
và Function
thông thường là gì?
Đó là Class
là constructible function nhưng không callable (nếu call như function bình thường sẽ báo TypeError
). Còn các function khác thì vừa constructible vừa callable (trừ arrow function, chỉ có thể call mà không thể construct). Vậy thực ra Class chỉ đơn giản chỉ là 1 constructor function, nhưng có cách khai báo khác với function thông thường mà thôi (cho phù hợp với các ngôn ngữ OOP khác).
Đọc kĩ định nghĩa ở lời mở đầu có thể thấy, hàm decor class return 1 constructor function
có thể thay thế class gốc → đối số được truyền vào chẳng phải chính là Class gốc đó sao!
Vậy khái niệm mà Typescript nhắc tới "the constructor of the decorated class" thực ra chính là class đó. Nó là 1 function chỉ có thể construct mà thôi!
Type Annotation của Constructor Function
Sau khi đã hiểu đối số truyền vào class decor là một construct-only function (class), việc tiếp cần làm đó là viết type cho nó.
Constructor Function, khi dùng với new, sẽ return 1 object có type là class đó
Vậy có thể mô tả function này trong typescript như sau:
class A {}
type ConstructorFunctionOfClassA = new () => A // nếu muốn linh hoạt hơn thì có thể dùng generic
type ConstructorFunctionOfClassX<X> = new () => X // và ta được kết quả tương tự
type ConstructorFunctionOfClassA = ConstructorFunctionOfClassX<A>
Cũng như các function
khác, vốn là 1 object
, Class
hay cái function mà “constructible nhưng không callable được” kia cũng có thể "viết" dưới dạng 1 object như sau:
class A {} // dạng object của nó
{
length: number,
name: string,
arguments: ...,
caller: ...,
prototype: object
// còn rất nhiều property ẩn khác tương tự các functions khác
}
Điều này dẫn đến việc, nếu ta muốn mô tả function
này bằng dạng object
, có thể dùng interface
hoặc type
như sau
interface ConstructorFunctionOfClassA {
length: number,
name: string,
new ():A
} // hoặc dùng
type ConstructorFunctionOfClassA = {
length: number,
name: string,
new ():A
}
2 cách khai báo type trên sử dụng cú pháp Construct Signatures trong typescript để mô tả 1 object có thể construct được (mà không call được), và type của object return
sau khi construct là gì. Ngoài ra còn có cú pháp Call Signatures (bỏ new
đi) nữa.
Cuối cùng, để mô tả Class
hay cái function mà “constructible nhưng không callable được” kia có thể có số lượng tham số tùy ý, cần sử dụng rest params
type ConstructorFunctionOfClassX<X> = new (...arg: any[]) => X
// or
interface ConstructorFunctionOfClassX<X> { new (...arg: any[]): X }
// or
type ConstructorFunctionOfClassX<X> = { new (...arg: any[]): X }
Tổng kết
Cuối cùng, sau khi mò mẫm, theo ý hiểu của mình, tôi đi đến kết luận:
Class
là 1 construct-only function, và nó có 1 type riêng. Cònobject
mà tạo ra từ nó, có type là tên củaClass
đó- Thứ truyền vào hàm decor 1 class, không phải là method
constructor
khi khai báo Class, mà chính làClass
hay construct-only function ấy
Tham khảo: https://www.typescriptlang.org/docs/handbook/decorators.html#class-decorators https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes