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

为何TypeScript类型收窄在回调函数中无法生效?

TypeScript类型收窄在回调函数中失效的问题

引发错误的代码

function upload(
    url: string,
    files: {
        file: File;
        fieldname: string;
    }[],
    options: {
        onProgress: Function;
        customData?: Record<string, any>;
        method?: string;
    } = {
        onProgress() {},
        method: "POST",
    }
) {
    options.customData &&
        Object.keys(options.customData).forEach(
            (fieldname) =>
                typeof options.customData[fieldname] !== "undefined" &&
                formData.append(fieldname, options.customData[fieldname])
        );
}

遇到的错误

'options.customData' is possibly 'undefined'. ts(18048)

按照TypeScript的类型收窄规则,options.customData && 本应将options.customData的类型从Record<string, any> | undefined收窄为Record<string, any>,但在forEach的回调函数内部访问时,该类型收窄不生效。

类型收窄有效的对比代码

function upload(
    url: string,
    files: {
        file: File;
        fieldname: string;
    }[],
    options: {
        onProgress: Function;
        customData?: Record<string, any>;
        method?: string;
    } = {
        onProgress() {},
        method: "POST",
    }
) {
    options.customData && options.customData["fieldname"];
}

原因分析

TypeScript的类型收窄作用域仅限于当前同步代码块。对于嵌套的回调函数,TypeScript无法保证回调执行时,options.customData的值没有被修改(哪怕变量是函数内的局部变量,回调的执行时机脱离了原收窄逻辑的同步上下文),因此不会保留外部代码块的类型收窄状态。

解决方案

1. 赋值给局部变量

将options.customData赋值给局部变量,TypeScript能跟踪该变量的类型收窄状态(局部变量不会被外部修改):

function upload(
    url: string,
    files: {
        file: File;
        fieldname: string;
    }[],
    options: {
        onProgress: Function;
        customData?: Record<string, any>;
        method?: string;
    } = {
        onProgress() {},
        method: "POST",
    }
) {
    const customData = options.customData;
    customData &&
        Object.keys(customData).forEach(
            (fieldname) =>
                typeof customData[fieldname] !== "undefined" &&
                formData.append(fieldname, customData[fieldname])
        );
}

2. 使用非空断言(谨慎使用)

如果你能100%确认回调执行时options.customData不会为undefined,可以用非空断言!跳过类型检查:

function upload(
    url: string,
    files: {
        file: File;
        fieldname: string;
    }[],
    options: {
        onProgress: Function;
        customData?: Record<string, any>;
        method?: string;
    } = {
        onProgress() {},
        method: "POST",
    }
) {
    options.customData &&
        Object.keys(options.customData).forEach(
            (fieldname) =>
                typeof options.customData![fieldname] !== "undefined" &&
                formData.append(fieldname, options.customData![fieldname])
        );
}

注意:非空断言会绕过TypeScript检查,若实际运行时变量变为undefined,会触发运行时错误,仅在确定变量状态时使用。

3. 自定义类型守卫

通过自定义类型守卫明确收窄类型,逻辑更严谨:

function isDefined<T>(value: T | undefined): value is T {
    return typeof value !== "undefined";
}

function upload(
    url: string,
    files: {
        file: File;
        fieldname: string;
    }[],
    options: {
        onProgress: Function;
        customData?: Record<string, any>;
        method?: string;
    } = {
        onProgress() {},
        method: "POST",
    }
) {
    if (isDefined(options.customData)) {
        Object.keys(options.customData).forEach(
            (fieldname) =>
                typeof options.customData[fieldname] !== "undefined" &&
                formData.append(fieldname, options.customData[fieldname])
        );
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 14:40:17