Określ typ zwrotu w funkcji strzałki TypeScript


118

Używam React i Redux i mam określone typy akcji jako interfejsy, dzięki czemu moje reduktory mogą korzystać z oznaczonych typów związków w celu zwiększenia bezpieczeństwa typów.

Mam więc deklaracje typów, które wyglądają następująco:

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

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

type TodoAction = AddTodoAction | DeleteTodoAction

Chciałbym stworzyć funkcje pomocnicze, które tworzą te akcje i zwykle używam do tego funkcji strzałkowych. Jeśli napiszę to:

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

Kompilator nie może zapewnić żadnej pomocy w upewnieniu się, że jest to poprawne, AddTodoActionponieważ typ zwracany nie został jawnie określony. Mogę jawnie określić typ powrotu, wykonując następujące czynności:

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

Ale wymaga to dwukrotnego określenia argumentów funkcji, więc jest to szczegółowe i trudniejsze do odczytania.

Czy istnieje sposób, w jaki mogę jawnie określić zwracany typ, używając notacji strzałkowej?

Myślałem o wypróbowaniu tego:

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

W tym przypadku kompilator wnioskuje teraz o zwracanym typie jako, AddTodoActionale nie sprawdza, czy zwracany obiekt ma wszystkie odpowiednie pola.

Mogę rozwiązać ten problem, przełączając się na inną składnię funkcji:

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

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

Każda z tych metod spowoduje, że kompilator użyje poprawnego typu zwracanego wyniku i wymusi prawidłowe ustawienie wszystkich pól, ale są one również bardziej szczegółowe i zmieniają sposób obsługi „ this” w funkcji (co może nie stanowić problemu, Przypuszczam.)

Czy jest jakaś rada, jak to zrobić najlepiej?


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

Odpowiedzi:


147

Najpierw rozważ następujący zapis z pierwotnego pytania:

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

Używając tej notacji, typujesz zwrócony obiekt do typu AddTodoAction. Jednak zadeklarowany typ zwracany przez funkcję jest nadal niezdefiniowany (a kompilator niejawnie przyjmie anyjako typ zwracany).

Zamiast tego użyj następującej notacji:

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

W takim przypadku pominięcie wymaganej właściwości spowoduje oczekiwany błąd kompilatora. Na przykład pominięcie textwłaściwości wygeneruje następujący (pożądany) błąd:

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"'.

Zobacz także przykład placu zabaw .


To jest dokładnie notacja, której szukałem. Niestety, składnia Visual Studio Code nie podkreśla tego w sposób, który odpowiada moim oczekiwaniom, ale kompilator wydaje się to rozumieć.
— Brian Hanechak

2

Myślę, że najlepiej jest stworzyć interfejs dla swojej funkcji, który ma odpowiednie typy, wtedy wystarczy określić tylko ten typ, a nie wszystkie zagnieżdżone typy interfejsu:

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

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

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

Aktualizacja: jak to zrobić z typami

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
jak można użyć leków generycznych w tym przykładzie? Powiedzmy AddTodoActionCreator <T>
— Jannic Beck

1
Co UserEventtu jest ?
— 1252748
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.