Typesafe action trong redux với Typescript

Vấn đề

Tình cờ bắt gặp bài viết Managing state in angular 2 app của Victor Savkin, mình chợt nhận ra Action của redux có thể được viết dưới dạng class của typescript để đảm bảo an toàn kiểu. Ví dụ ta không thể truy cập vào một property không tồn tại của một action, chẳng hạn:

class AddTodoAction {
    public text: string;
}

/*reducer*/
const todos = (state, action) => {
    if ( /*type of action is AddTodoAction*/ ) {
         let id = action.id; // Báo lỗi, truy cập vào property không tồn tại trong action
    }
    //...
}

Vsavkin's
Vsavkin's

Tuy nhiên cách kiểm tra của @vsavkin, dùng instanceof, lại không được khuyến khích, như @gaearon đã chỉ ra rằng tính năng record, replay action của redux sẽ serialize action thành JSON và ngược lại, khi đó thông tin về class sẽ bị mất. Như vậy cuối cùng vẫn phải chỉ định property type của action một cách tường minh.

Như vậy chúng ta phải dùng một cách nào đó thay vì dùng instanceof mà vẫn đảm bảo an toàn kiểu.

Typescript

Vì chúng ta lười, không muốn viết type = 'x'; trong tất cá các action, và vì chúng ta pro nên chúng ta sẽ sử dụng những công cụ thời thượng nhất.

Decorator

Decorator là một hàm bậc cao, nhận vào một hàm và trả lại một hàm có thêm vài behaviour mới. Ở đây chúng ta thêm property type vào prototype của hàm.

Hãy tưởng tượng chúng ta sẽ sử dụng decorator như thế này để thêm type add todo vào class AddTodoAction.

@ActionType('AddTodo')
class AddTodoAction extends Action {

}

Một function cho phép thêm property vào object được decorate, ở đây là type.

/*định  nghĩa giao diện của lớp action có prototype*/
interface IAction<T extends Action> {
  prototype: T;
}

/*abstract class Action tự set type của instance = prototype.type*/
export abstract class Action {
  type: string;

  constructor() {
    this.type = this.type;
  }
}

/* decorator set prototype.type = name */
export function ActionType(name: string) {
  return function<T extends Action>(actionClass: IAction<T>) {
    actionClass.prototype.type = name;
  }
}

Type guard

Type guard cho phép bộ dịch của typescript ngăn chặn các hành động không "đúng đắn" với các object. Ví dụ:

if (typeof a === 'string') {
    return a.substr(0, 3); // OK
} else {
    return a.length // Error, không có gì đảm bảo a có property length
}

Typescript cho phép dùng typeofinstanceof làm type guard. Ngoài ra có thế tự định nghĩa type guard. Đây là thứ chúng ta có thế dùng.

Định nghĩa một type guard rất đơn giản, như một hàm và kiểu trả về là arg is T

export function isActionType<T extends Action>(
  action: Action,
  actionClass: IAction<T>
): action is T {
  return action.type === actionClass.prototype.type;
}

Done, vậy là đã xong, chúng ta có thế sử dụng như thế này:

// Định nghĩa action
@ActionType('ToogleTodo')
export class ToogleTodoAction extends Action {
  constructor(public _id: string) {
    super();
  }
}


// Trong reducer
function todos(state, action) {
    if (isActionType(action, ToggleTodoAction)) {
        return state.map(todo => {

           // truy cập hợp lệ vì action được đảm bảo có prop _id
           if (todo._id === action._id) { 
                todo.completed = !todo.completed;
           }

           return todo;
        });
    }

    return state;
}

Nguồn:

  • Issue này chứa nhiều bình luận hữu ích #
  • Về type guards #

Cross-posted on my blog

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Hoàng Duy

25 bài viết.
66 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
43 8
Xin chào, đây là lần đầu tiên mình post bài ở đây. Nhiều vấn đề mình cũng không rành lắm, có gì sai mọi người góp ý nhé. Xin cảm ơn :D Bài này gi...
Hoàng Duy viết hơn 3 năm trước
43 8
White
33 8
Lâu không post gì muốn viết một bài dài dài về js cơ mà đau đầu quá viết mãi không xong, thôi post bài ngắn vậy :smiley: Lấy screen size ở đây tôi...
Hoàng Duy viết 3 năm trước
33 8
White
32 3
Đây là một trong các concept mới đối tượng mới được đưa vào ECMAScript 6. Việc sử dụng chúng rất dễ nhưng để hiểu được thì (đối với tôi) cũng cần k...
Hoàng Duy viết hơn 3 năm trước
32 3
Bài viết liên quan
White
4 1
Trải qua 1 thời gian khá dài trải nghiệm Angular, mình thấy RxJS khá hay. Bài này sẽ quay trở lại với bài toán Counter thần kì của React/Redux, như...
cdxf viết 3 tháng trước
4 1
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
25 bài viết.
66 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!