web-dev-qa-db-fra.com

Spécifiez le type de retour dans la fonction de flèche TypeScript

J'utilise React et Redux et des types d'action sont spécifiés en tant qu'interfaces, de sorte que mes réducteurs puissent tirer parti des types d'union étiquetés pour améliorer la sécurité des types.

Donc, j'ai des déclarations de type qui ressemblent à ceci:

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

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

type TodoAction = AddTodoAction | DeleteTodoAction

Je voudrais faire des fonctions d'aide qui créent ces actions, et j'ai tendance à utiliser des fonctions de flèche pour cela. Si j'écris ceci:

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

Le compilateur ne peut fournir aucune aide pour s'assurer qu'il s'agit d'un AddTodoAction valide, car le type de retour n'est pas spécifié explicitement. Je peux spécifier explicitement le type de retour en faisant ceci:

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

Mais cela nécessite de spécifier deux fois les arguments de ma fonction, donc c'est plus compliqué et plus difficile à lire.

Existe-t-il un moyen de spécifier explicitement le type de retour en utilisant la notation en flèche?

J'ai pensé à essayer ceci:

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

Dans ce cas, le compilateur déduit maintenant le type de retour sous la forme AddTodoAction mais il ne valide pas que l'objet que je retourne possède tous les champs appropriés.

Je pourrais résoudre ce problème en utilisant une syntaxe de fonction différente:

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

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

L’une ou l’autre de ces méthodes oblige le compilateur à utiliser le type de retour correct et à appliquer le fait que j’ai défini tous les champs de manière appropriée, mais ils sont également plus détaillés et modifient la manière dont 'this' est gérée dans une fonction ne soit pas un problème, je suppose.)

Y a-t-il des conseils sur la meilleure façon de procéder?

72
Brian Hanechak

Premièrement, considérons la notation suivante de votre question initiale:

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

A l'aide de cette notation, vous convertissez l'objet renvoyé dans le type AddTodoAction. Cependant, le type de retour déclaré de la fonction n'est toujours pas défini (et le compilateur supposera implicitement que any est le type de retour).

Utilisez plutôt la notation suivante:

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

Dans ce cas, l'omission d'une propriété requise produira l'erreur de compilateur attendue. Par exemple, l'omission de la propriété text générera l'erreur suivante (souhaitée):

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

Voir aussi le exemple de terrain de je .

97
helmbert

Je pense que votre meilleur pari est de créer une interface pour votre fonction qui a les bons types, alors il vous suffit de spécifier ce type, pas tous les types imbriqués de votre interface:

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

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

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

Mise à jour: Comment faire cela avec les types

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}; };
1
azium