Geben Sie den Rückgabetyp in der TypeScript-Pfeilfunktion an


118

Ich verwende React und Redux und habe Aktionstypen als Schnittstellen angegeben, damit meine Reduzierer markierte Vereinigungstypen für eine verbesserte Typensicherheit nutzen können.

Ich habe also Typdeklarationen, die so aussehen:

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

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

type TodoAction = AddTodoAction | DeleteTodoAction

Ich möchte Hilfsfunktionen erstellen, die diese Aktionen erstellen, und ich neige dazu, dafür Pfeilfunktionen zu verwenden. Wenn ich das schreibe:

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

Der Compiler kann keine Hilfe bereitstellen, um sicherzustellen, dass dies gültig ist, AddTodoActionda der Rückgabetyp nicht explizit angegeben wird. Ich kann den Rückgabetyp explizit angeben, indem ich dies tue:

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

Dafür müssen meine Funktionsargumente jedoch zweimal angegeben werden, damit sie ausführlich und schwerer zu lesen sind.

Gibt es eine Möglichkeit, den Rückgabetyp bei Verwendung der Pfeilnotation explizit anzugeben?

Ich habe darüber nachgedacht, dies zu versuchen:

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

In diesem Fall AddTodoActionleitet der Compiler jetzt den Rückgabetyp als ab , überprüft jedoch nicht, ob das von mir zurückgegebene Objekt alle entsprechenden Felder enthält.

Ich könnte dies lösen, indem ich zu einer anderen Funktionssyntax wechsle:

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

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

Jede dieser Methoden veranlasst den Compiler, den richtigen Rückgabetyp zu verwenden und zu erzwingen, dass ich alle Felder entsprechend festgelegt habe. Sie sind jedoch auch ausführlicher und ändern die Art und Weise, wie ' this' in einer Funktion behandelt wird (was möglicherweise kein Problem darstellt). Ich nehme an.)

Gibt es Ratschläge, wie dies am besten funktioniert?


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

Antworten:


147

Betrachten Sie zunächst die folgende Notation aus Ihrer ursprünglichen Frage:

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

Mit dieser Notation geben Sie das zurückgegebene Objekt in den Typ ein AddTodoAction. Der deklarierte Rückgabetyp der Funktion ist jedoch immer noch undefiniert (und der Compiler nimmt implizit den anyRückgabetyp an).

Verwenden Sie stattdessen die folgende Notation:

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

In diesem Fall führt das Weglassen einer erforderlichen Eigenschaft zum erwarteten Compilerfehler. Wenn Sie beispielsweise die textEigenschaft weglassen, wird der folgende (gewünschte) Fehler generiert:

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

Siehe auch das Spielplatzbeispiel .


Dies ist genau die Notation, nach der ich gesucht habe. Leider hebt die Visual Studio-Code-Syntax dies nicht in einer Weise hervor, die meinen Erwartungen entspricht, aber der Compiler scheint es zu verstehen.
— Brian Hanechak

1

Ich denke, Ihre beste Wahl ist es, eine Schnittstelle für Ihre Funktion zu erstellen, die die richtigen Typen hat. Dann müssen Sie nur diesen Typ angeben, nicht alle verschachtelten Typen Ihrer Schnittstelle:

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

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

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

Update: So geht's mit Typen

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
Wie würde man in diesem Beispiel Generika verwenden? Sagen wir AddTodoActionCreator <T>
— Jannic Beck

1
Was ist UserEventhier
— 1252748
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.