You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3组合API中如何包装Promise并保留原函数返回类型?

问题:让useForm的makeRequest返回与传入请求函数一致的类型

我有一个基于Axios实例的register函数,类型定义如下:

async function register(data: RegisterData): Promise<AxiosResponse<UserResponse, any>>

实现代码:

export const register = (data: RegisterData) => api.post<UserResponse>('register', data)

我用Vue3组合API封装了表单逻辑useForm函数,接收表单初始值和请求函数(比如register):

import {reactive, ref} from 'vue'
import Errors from '../helpers/Errors'
import type {AxiosResponse} from 'axios'

export function useForm<T extends Record<string, any>, R extends (...args: any) => Promise<AxiosResponse>>(init: T, request: R) {
    const form = reactive<T>(init)
    const errors = reactive(new Errors())
    const isLoading = ref(false)

    const makeRequest = () => {
        isLoading.value = true
        errors.clear()
        return request(form)
            .then(response => Promise.resolve(response))
            .catch(e => {
                if (e.response.status === 422) {
                    errors.save(e.response.data.errors)
                }
                return Promise.reject(e)
            })
            .finally(() => isLoading.value = false)
    }

    return {form, errors, isLoading, makeRequest}
}

在RegisterPage.vue中调用时:

import {useForm} from '../../../composables/form'
import {register} from '../../../api/auth'

const {form, isLoading, errors, makeRequest} = useForm({
  username: null,
  password: null,
  password_confirmation: null,
  referral_code: null
}, register)
makeRequest() // 当前返回类型:Promise<AxiosResponse<any, any>>,期望返回:Promise<AxiosResponse<UserResponse, any>>

如何让makeRequest的返回类型与传入的请求函数(如register)完全一致?


解决方案

问题出在useForm的泛型定义上:原代码中R的返回类型Promise<AxiosResponse>没有保留具体的泛型参数(即UserResponse和错误类型),导致TypeScript无法推断出准确的返回类型。

修改useForm的泛型定义,明确请求函数的参数类型和返回类型的泛型信息,就能让makeRequest继承传入请求函数的完整类型:

import {reactive, ref} from 'vue'
import Errors from '../helpers/Errors'
import type {AxiosResponse} from 'axios'

// 重新定义泛型参数,明确表单数据、响应数据、错误数据的类型
export function useForm<
  FormData extends Record<string, any>,
  ResponseData,
  ErrorData = any
>(
  init: FormData,
  request: (data: FormData) => Promise<AxiosResponse<ResponseData, ErrorData>>
) {
    const form = reactive<FormData>(init)
    const errors = reactive(new Errors())
    const isLoading = ref(false)

    // 明确makeRequest的返回类型与请求函数一致
    const makeRequest = (): Promise<AxiosResponse<ResponseData, ErrorData>> => {
        isLoading.value = true
        errors.clear()
        return request(form)
            .then(response => response) // 无需额外Promise.resolve,直接返回即可
            .catch(e => {
                // 增加可选链避免e.response为undefined时报错
                if (e.response?.status === 422) {
                    errors.save(e.response.data.errors)
                }
                return Promise.reject(e)
            })
            .finally(() => isLoading.value = false)
    }

    return {form, errors, isLoading, makeRequest}
}

关键修改点:

  • 泛型参数细化:新增ResponseData和ErrorData泛型,让请求函数的返回类型携带具体的响应和错误类型信息。
  • 请求函数类型约束:明确request参数必须接收FormData类型的参数,返回带有具体泛型的AxiosResponse。
  • 明确makeRequest返回类型:直接指定其返回类型与request的返回类型一致,TypeScript会自动推断ResponseData和ErrorData的具体类型(比如传入register时,ResponseData会被推断为UserResponse)。

修改后,调用makeRequest()时,返回类型会自动匹配register的Promise<AxiosResponse<UserResponse, any>>,符合预期。


内容的提问来源于stack exchange,提问作者Seagle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 21:27:27