在TypeScript箭头函数中指定返回类型

时间:2016-10-26 19:12:15

标签: typescript

我正在使用React和Redux并将操作类型指定为接口,以便我的reducer可以利用标记的union类型来提高类型安全性。

所以,我的类型声明看起来像这样:

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”的方式(我想这可能不是问题。)

对于最好的方法有什么建议吗?

3 个答案:

答案 0 :(得分:86)

首先,请考虑原始问题中的以下符号:

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

另见playground example

答案 1 :(得分:1)

我认为您最好的选择是为您的函数创建一个具有正确类型的接口,然后您只需要指定该类型,而不是所有嵌套类型的接口:

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 :(得分:0)

有两种方法可以通过正确的输入和最少的代码实现这一点:

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

// Because the this keyword works different in arrow functions these 
// 2 implementations are different in some cases:

// arrow function form/ function expression
const addTodo1 = (text: string): AddTodoAction => ({
    type: "ADD_TODO",
    text: text
})

// function declaration form
function addTodo2 (text: string): AddTodoAction {
    return ({
        type: "ADD_TODO",
        text: text
    })
}

现在 TS 编译器可以检查返回的类型。例如:

const todo = addTodo1('hi');

// Following gives TS compile time error
// addTodo1 returns AddTodoAction which does not have id on the type

const id = todo.id // Property 'id' does not exist on type 'AddTodoAction'.