【问题标题】:Specify return type in TypeScript arrow function在 TypeScript 箭头函数中指定返回类型
【发布时间】:2017-03-09 07:10:12
【问题描述】:

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

所以,我的类型声明如下所示:

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

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

【问题讨论】:

  • getTitle = ():string =&gt; 'State Lists'

标签: typescript


【解决方案1】:

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

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

【讨论】:

  • 这正是我正在寻找的符号。不幸的是,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}; 
};

【讨论】:

  • 在这个例子中如何使用泛型?假设 AddTodoActionCreator
  • 这里的UserEvent 是什么?
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 2021-12-31
    • 1970-01-01
    • 2018-01-25
    • 2022-01-22
    • 2021-07-16
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多