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

TypeScript中如何依托ready状态自动窄化可选参数类型?

问题解决:自动窄化useRequest回调中的参数类型

原代码与报错

// 第三方库
type AnyFn = (...args: any[]) => any;
type UseRequestOptions = {
    ready?: boolean;
}
const useRequest = (fn: AnyFn, options: UseRequestOptions) => {
    const ready = options.ready ?? true;
    if (ready) {
        fn()
    }
}

// 业务代码
const api = {
    getUser(id: number) { }
}
const useGetUserById = (id: number | undefined) => useRequest(() => api.getUser(id), { ready: !!id })

const id: number | undefined = 1;
useGetUserById(1)

编译时TypeScript抛出错误:

Argument of type 'number | undefined' is not assignable to parameter of type 'number'.
Type 'undefined' is not assignable to type 'number'.(2345)

需求:不使用类型断言(如id as number),通过修改第三方库或用户侧代码,让api.getUser接收的id自动窄化为number类型。


解决方案

方案1:修改第三方库的类型定义(最优雅)

原库的AnyFn类型太宽泛,无法关联ready状态和回调函数的参数合法性。可以重构类型,让ready和回调参数建立依赖:

// 修改后的第三方库代码
type UseRequestOptions<T extends unknown[] = []> = {
    ready?: boolean;
    args?: T; // 存储回调所需的参数,仅在ready为true时传递
}

const useRequest = <T extends unknown[], R>(
    fn: (...args: T) => R,
    options: UseRequestOptions<T>
) => {
    const ready = options.ready ?? true;
    if (ready) {
        // 断言参数存在:因为ready为true时,业务侧会确保参数合法
        fn(...(options.args as T));
    }
}

业务侧使用时,TypeScript会自动推导参数类型:

const useGetUserById = (id: number | undefined) => 
    useRequest(api.getUser, { 
        ready: !!id,
        args: id ? [id] : undefined // 仅当id存在时传参
    })

此时TypeScript能识别:当ready为true时,args必然是[number],不会触发类型错误。

方案2:用户侧不修改库,利用控制流分析

在回调函数内部加类型守卫,让TypeScript自动窄化id的类型:

const useGetUserById = (id: number | undefined) => 
    useRequest(() => {
        // 控制流分析自动将id窄化为number
        if (id) api.getUser(id);
    }, { ready: !!id })

因为ready为true的条件是!!id,只有id存在时才会执行回调,所以if (id)分支必然会命中,不会有运行时问题,编译也能通过。

方案3:用函数重载约束入参

给useGetUserById加重载,明确不同入参类型的处理逻辑:

// 重载定义
function useGetUserById(id: number): void;
function useGetUserById(id: undefined): void;
// 实现
function useGetUserById(id: number | undefined) {
    return useRequest(() => {
        if (id) api.getUser(id);
    }, { ready: !!id });
}

这样用户调用时,TypeScript会根据入参类型做正确推断,同时回调内部的id在执行时必然是number。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 03:54:57