Tuple
Function của TypeScript chỉ có thể trả về 1 giá trị. Tuy nhiên, thực tế có lúc muốn trả về nhiều giá trị. Trong trường hợp đó, có thể đặt tất cả giá trị muốn trả về vào mảng và return.
Giá trị trả về của function sau là hằng số, nhưng hãy hiểu như kết quả tính toán thực tế.
tsfunctiontuple () {//...return [1, "ok", true];}
tsfunctiontuple () {//...return [1, "ok", true];}
Vấn đề của mảng
Trong ví dụ trên, kiểu nào là phù hợp cho giá trị trả về? Nếu bạn đã đọc từ trang mảng, có thể nghĩ đến any[] hoặc unknown[] vì đây là kiểu có thể chứa bất kỳ thứ gì.
tsconstlist : unknown[] =tuple ();Object is of type 'unknown'.2571Object is of type 'unknown'.list [0].toString ();
tsconstlist : unknown[] =tuple ();Object is of type 'unknown'.2571Object is of type 'unknown'.list [0].toString ();
Tuy nhiên, không thể gọi method từ list[n]. Vì mỗi phần tử của list là unknown.
Vậy có nên dùng any[] làm kiểu giá trị trả về không? Điều đó cũng có vấn đề. Đang dùng TypeScript để tận hưởng lợi ích của kiểu, nhưng ở đây lại code như thể không có kiểu thì nhạt nhẽo. Đó là lúc có thể dùng tuple.
Kiểu của tuple
Kiểu của tuple đơn giản, chỉ cần viết [] và đặt kiểu bên trong. Nghĩa là, function tuple() ở trên có thể nói là có giá trị trả về như sau.
tsconstlist : [number, string, boolean] =tuple ();
tsconstlist : [number, string, boolean] =tuple ();
Tương tự có thể viết ở giá trị trả về của function.
tsfunctiontuple (): [number, string, boolean] {//...return [1, "ok", true];}
tsfunctiontuple (): [number, string, boolean] {//...return [1, "ok", true];}
Kiểu mảng có 2 cách viết T[] và Array<T> nhưng tuple chỉ có cách viết này.
Đặt label cho tuple
Khi function trả về tuple toàn giá trị cùng kiểu, có thể khó hiểu kiểu đó biểu thị điều gì. Trong trường hợp đó, có thể đặt label cho tuple.
tsconstcoordinate : [x : number,y : number,z : number] =tuple ();
tsconstcoordinate : [x : number,y : number,z : number] =tuple ();
Với ví dụ này, dễ hiểu rằng đây là giá trị trả về của function trả về tọa độ 3 chiều.
Truy cập tuple
Biến nhận tuple có thể sử dụng trực tiếp property và method của kiểu bên trong.
tsconstlist : [number, string, boolean] =tuple ();list [0].toExponential ();list [1].length ;list [2].valueOf ();
tsconstlist : [number, string, boolean] =tuple ();list [0].toExponential ();list [1].length ;list [2].valueOf ();
Biến nhận tuple không thể truy cập phần tử ngoài phạm vi đã định nghĩa trong tuple.
tsconstlist : [number, string, boolean] =tuple ();Tuple type '[number, string, boolean]' of length '3' has no element at index '5'.2493Tuple type '[number, string, boolean]' of length '3' has no element at index '5'.list [5 ];
tsconstlist : [number, string, boolean] =tuple ();Tuple type '[number, string, boolean]' of length '3' has no element at index '5'.2493Tuple type '[number, string, boolean]' of length '3' has no element at index '5'.list [5 ];
Do đó, ngay cả khi thực hiện thao tác tăng phần tử mảng như list.push(), cũng không thể sử dụng phần tử đó.
Truy cập tuple có label
Label chỉ được dùng để cải thiện khả năng đọc, không thể dùng label để truy cập trong code thực tế.
tsconstcoordinate : [x : number,y : number,z : number] =tuple ();coordinate [0];Property 'x' does not exist on type '[x: number, y: number, z: number]'.2339Property 'x' does not exist on type '[x: number, y: number, z: number]'.coordinate .; x
tsconstcoordinate : [x : number,y : number,z : number] =tuple ();coordinate [0];Property 'x' does not exist on type '[x: number, y: number, z: number]'.2339Property 'x' does not exist on type '[x: number, y: number, z: number]'.coordinate .; x
Truy cập tuple bằng destructuring assignment
Giá trị trả về của function tuple() có thể nhận bằng destructuring assignment như sau.
tsconst [num ,str ,bool ]: [number, string, boolean] =tuple ();
tsconst [num ,str ,bool ]: [number, string, boolean] =tuple ();
Ngoài ra, nếu chỉ cần một số giá trị trả về nhất định, chỉ viết , mà không viết tên biến.
tsconst [, ,bool ]: [number, string, boolean] =tuple ();
tsconst [, ,bool ]: [number, string, boolean] =tuple ();
Trường hợp sử dụng tuple
Khi lập trình bất đồng bộ trong TypeScript, có lúc muốn thực hiện các xử lý tốn thời gian song song thay vì tuần tự. Lúc đó TypeScript sử dụng Promise.all(). Đây là lúc tuple hữu ích.
Giải thích chi tiết về Promise có trang chuyên đề trong sách này. Ở đây chỉ cần nhớ rằng biến kiểu Promise<T> khi đặt await phía trước sẽ lấy ra được T. Ngoài ra, T này được gọi là generics, cũng có trang chuyên đề.
📄️ Xử lý bất đồng bộ
Nếu bạn muốn xây dựng một ứng dụng JavaScript nghiêm túc, bạn sẽ không thể tách rời khỏi xử lý bất đồng bộ. Ban đầu có thể khó hiểu, nhưng giờ đây JavaScript đã có các tính năng giúp thao tác với xử lý bất đồng bộ một cách trực quan hơn nhiều, làm giảm đáng kể rào cản.
📄️ Generics
Việc kết hợp giữa type safety và code reusability là một thách thức. Nếu cố gắng sử dụng cùng một đoạn code cho nhiều kiểu dữ liệu khác nhau, type safety sẽ bị hy sinh. Ngược lại, nếu tập trung vào type safety, bạn sẽ phải viết nhiều đoạn code tương tự nhau, khiến code reusability khó đạt được. Generics là tính năng được giới thiệu để giải quyết vấn đề này. Với generics, bạn có thể đồng thời đảm bảo type safety và code reusability.
tsconstpromise :Promise <number> =yyAsync ();constnum : number = awaitpromise ;
tsconstpromise :Promise <number> =yyAsync ();constnum : number = awaitpromise ;
Ví dụ, giả sử có 2 function takes3Seconds(), takes5Seconds() mất 3 giây và 5 giây để xử lý.
tsasync functiontakes3Seconds ():Promise <string> {// ...return "finished!";}async functiontakes5Seconds ():Promise <number> {// ...return -1;}
tsasync functiontakes3Seconds ():Promise <string> {// ...return "finished!";}async functiontakes5Seconds ():Promise <number> {// ...return -1;}
Nếu thực thi trực tiếp các function này sẽ mất 3 + 5 = 8 giây.
tsconststr : string = awaittakes3Seconds ();constnum : number = awaittakes5Seconds ();
tsconststr : string = awaittakes3Seconds ();constnum : number = awaittakes5Seconds ();
Sử dụng Promise.all() có thể viết như sau. Thời gian mất là thời gian của function lâu nhất, tức là 5 giây.
tsconsttuple : [string, number] = awaitPromise .all ([takes3Seconds (),takes5Seconds (),]);
tsconsttuple : [string, number] = awaitPromise .all ([takes3Seconds (),takes5Seconds (),]);
Lúc này biến tuple nhận giá trị trả về của Promise.all() có kiểu [string, number]. Phần generics Promise<T> của các function thực thi và thứ tự kiểu của tuple là nhất quán. Nghĩa là nếu đổi chỗ như sau, sẽ nhận được tuple [number, string] đã đổi chỗ.
tsconsttuple : [number, string] = awaitPromise .all ([takes5Seconds (),takes3Seconds (),]);
tsconsttuple : [number, string] = awaitPromise .all ([takes5Seconds (),takes3Seconds (),]);
Promise.all() không lưu vào tuple giá trị trả về theo thứ tự function kết thúc trước, mà giữ nguyên thứ tự ban đầu. Không phải vì take3seconds() kết thúc sớm hơn nên được lưu vào tuple trước, mà kiểu phần tử của tuple tuple được quyết định theo thứ tự truyền vào argument.