Chỉ định kiểu trả về trong hàm mũi tên TypeScript


118

Tôi đang sử dụng React và Redux và có các kiểu hành động được chỉ định làm giao diện, để các trình giảm bớt của tôi có thể tận dụng các kiểu liên minh được gắn thẻ để cải thiện độ an toàn của kiểu.

Vì vậy, tôi có các khai báo kiểu như sau:

interface AddTodoAction {
    type: "ADD_TODO",
    text: string
};

interface DeleteTodoAction {
    type: "DELETE_TODO",
    id: number
}

type TodoAction = AddTodoAction | DeleteTodoAction

Tôi muốn tạo các hàm trợ giúp tạo ra các hành động này và tôi có xu hướng sử dụng các hàm mũi tên cho việc này. Nếu tôi viết cái này:

export const addTodo1 = (text: string) => ({
    type: "ADD_TODO",
    text
});

Trình biên dịch không thể cung cấp bất kỳ trợ giúp nào trong việc đảm bảo đây là giá trị hợp lệ AddTodoActionvì kiểu trả về không được chỉ định rõ ràng. Tôi có thể chỉ định kiểu trả về một cách rõ ràng bằng cách thực hiện điều này:

export const addTodo2: (text: string) => AddTodoAction = (text: string) => ({
    type: "ADD_TODO",
    text
})

Nhưng điều này yêu cầu xác định các đối số hàm của tôi hai lần, vì vậy nó dài dòng và khó đọc hơn.

Có cách nào tôi có thể chỉ định kiểu trả về một cách rõ ràng khi sử dụng ký hiệu mũi tên không?

Tôi đã nghĩ đến việc thử điều này:

export const addTodo3 = (text: string) => <AddTodoAction>({
    type: "ADD_TODO",
    text
})

Trong trường hợp này, trình biên dịch bây giờ suy ra kiểu trả về AddTodoActionnhưng nó không xác thực rằng đối tượng tôi đang trả về có tất cả các trường thích hợp.

Tôi có thể giải quyết vấn đề này bằng cách chuyển sang một cú pháp hàm khác:

export const addTodo4 = function(text: string): AddTodoAction {
    return {
        type: "ADD_TODO",
        text
    }
}

export function addTodo5(text: string): AddTodoAction {
    return {
        type: "ADD_TODO",
        text
    }
}

Một trong hai phương pháp này sẽ khiến trình biên dịch sử dụng đúng kiểu trả về và thực thi rằng tôi đã đặt tất cả các trường một cách thích hợp, nhưng chúng cũng dài dòng hơn và chúng thay đổi cách ' this' được xử lý trong một hàm (có thể không phải là vấn đề, Tôi giả sử.)

Có lời khuyên nào về cách tốt nhất để làm điều này?


6
getTitle = ():string => 'State Lists'
Vikas Bansal

Câu trả lời:


147

Trước tiên, hãy xem xét ký hiệu sau từ câu hỏi ban đầu của bạn:

export const addTodo3 = (text: string) => <AddTodoAction>({
    type: "ADD_TODO",
    text
})

Sử dụng ký hiệu này, bạn nhập đối tượng trả về vào kiểu AddTodoAction. Tuy nhiên, kiểu trả về được khai báo của hàm vẫn chưa được xác định (và trình biên dịch sẽ ngầm định anylà kiểu trả về).

Sử dụng ký hiệu sau để thay thế:

export const addTodo3 = (text: string): AddTodoAction => ({
    type: "ADD_TODO",
    text: text
})

Trong trường hợp này, việc bỏ qua một thuộc tính bắt buộc sẽ gây ra lỗi trình biên dịch như mong đợi. Ví dụ: bỏ qua thuộc texttính sẽ tạo ra lỗi (mong muốn) sau:

Type '{ type: "ADD_TODO"; }' is not assignable to type 'TodoAction'.
  Type '{ type: "ADD_TODO"; }' is not assignable to type 'DeleteTodoAction'.
    Types of property 'type' are incompatible.
      Type '"ADD_TODO"' is not assignable to type '"DELETE_TODO"'.

Cũng xem ví dụ về sân chơi .


Đây chính xác là ký hiệu mà tôi đang tìm kiếm. Thật không may, Visual Studio Code không làm nổi bật cú pháp này theo cách phù hợp với mong đợi của tôi, nhưng trình biên dịch dường như hiểu nó.
Brian Hanechak

2

Tôi nghĩ cách tốt nhất của bạn là tạo một giao diện cho chức năng của bạn có các loại phù hợp, sau đó bạn chỉ cần chỉ định loại đó, không phải tất cả các loại giao diện lồng nhau của bạn:

interface AddTodoAction {
    type: "ADD_TODO",
    text: string
};

interface AddTodoActionCreator {
    (text: string): AddTodoAction;
};

export const addTodo: AddTodoActionCreator = (text) => ({
    type: "ADD_TODO",
    text
});

Cập nhật: Cách thực hiện việc này với các loại

export interface GeneralAction<T> {
    type: string;
    payload: T;
}

export interface GeneralActionCreator<T> {
    (payload: T): GeneralAction<T>;
}

export const SAVE_EVENT = 'SAVE_EVENT';

export const SaveEvent: GeneralActionCreator<UserEvent> = (payload) => { return {type: SAVE_EVENT, payload}; };

2
làm thế nào để sử dụng generic trong ví dụ này? Cho phép nói AddTodoActionCreator <T>
Jannic Beck

1
Cái gì UserEventở đây?
1252748 20/09/19
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.