Укажите тип возвращаемого значения в функции стрелки TypeScript


118

Я использую React и Redux, и у меня есть типы действий, указанные как интерфейсы, чтобы мои редукторы могли использовать преимущества помеченных типов объединения для повышения безопасности типов.

Итак, у меня есть объявления типов, которые выглядят так:

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

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

type TodoAction = AddTodoAction | DeleteTodoAction

Я хотел бы создать вспомогательные функции, которые создают эти действия, и я обычно использую для этого стрелочные функции. Если я напишу это:

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

Компилятор не может помочь убедиться в том, что это допустимо, AddTodoActionпоскольку тип возвращаемого значения не указан явно. Я могу явно указать возвращаемый тип, выполнив следующие действия:

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

Но для этого требуется дважды указать аргументы моей функции, поэтому он будет подробным и трудным для чтения.

Есть ли способ явно указать тип возвращаемого значения при использовании обозначения стрелок?

Я подумал о том, чтобы попробовать это:

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

В этом случае компилятор теперь определяет возвращаемый тип как, AddTodoActionно не проверяет, имеет ли возвращаемый объект все соответствующие поля.

Я мог бы решить эту проблему, переключившись на другой синтаксис функции:

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

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

Любой из этих методов заставит компилятор использовать правильный тип возвращаемого значения и обеспечить, чтобы я правильно установил все поля, но они также более подробны и меняют способ thisобработки в функции (что может не быть проблемой, Я полагаю.)

Есть ли какой-нибудь совет, как это лучше сделать?


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

Ответы:


147

Во-первых, рассмотрим следующие обозначения из вашего исходного вопроса:

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

Используя эту нотацию, вы приводите возвращаемый объект к типу AddTodoAction. Однако объявленный возвращаемый тип функции все еще не определен (и компилятор неявно принимает в anyкачестве возвращаемого типа).

Вместо этого используйте следующие обозначения:

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

В этом случае пропуск обязательного свойства приведет к ожидаемой ошибке компилятора. Например, отсутствие textсвойства приведет к следующей (желательной) ошибке:

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

Также см. Пример игровой площадки .


Это именно то обозначение, которое я искал. К сожалению, синтаксис Visual Studio Code не выделяет это так, как я ожидал, но компилятор, похоже, это понимает.
— Брайан Ханечак,

2

Я думаю, что лучше всего создать интерфейс для вашей функции, который имеет правильные типы, тогда вам нужно указать только этот тип, а не все вложенные типы вашего интерфейса:

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

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

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

Обновление: как это сделать с типами

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
как использовать дженерики в этом примере? Допустим, AddTodoActionCreator <T>
— Янник Бек

1
Что UserEventздесь?
— 1252748
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.