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

TypeScript数组与对象的类型安全迭代类型检查问题

TypeScript通用迭代方法类型检查错误修复

我编写了一个可接收数组或对象的通用迭代方法,但无法通过TypeScript的类型检查。以下是实现代码及遇到的错误:

export type IterateArg = {[key:string]:any}|any[];

export type IterateKey<T extends IterateArg> = T extends any[] ? number : keyof T;


export const iterate = <Type extends IterateArg>(obj_or_array: Type, callback: (value:any,key: IterateKey<Type> )=>any) => {

    if ( Array.isArray(obj_or_array)) {
        obj_or_array.forEach(callback); // 错误#1

    
    }else{

        for (var key in obj_or_array) {
            if (obj_or_array.hasOwnProperty(key)) {
                callback(obj_or_array[key], key); // 错误#2
            }
        }
    }
    
};

function arrayCallback(value:string, key:number){}

iterate(['a','b'],arrayCallback);

function objectCallback(value:string, key:string){}

iterate({a:'a',b:'b'},objectCallback);

错误信息

  • 错误#1:参数类型“(value: any, key: IterateKey) => any”无法赋值给类型“(value: any, index: number, array: any[]) => void”。参数“key”和“index”的类型不兼容,类型“number”无法赋值给类型“IterateKey”。
  • 错误#2:参数类型“Extract<keyof Type, string>”无法赋值给类型“IterateKey”。类型“string & keyof Type”无法赋值给类型“IterateKey”。

问题原因与修复方案

核心问题是TypeScript无法在分支逻辑中准确推断泛型Type的具体类型,导致IterateKey<Type>和实际分支中的类型不匹配。以下是两种可行的修复方式:

方案1:使用函数重载(推荐)

通过为数组和对象分别定义明确的重载签名,让TypeScript在调用时精准匹配参数类型:

// 定义重载签名,明确数组与对象的回调类型
export function iterate<T extends any[]>(arr: T, callback: (value: T[number], key: number) => any): void;
export function iterate<T extends Record<string, any>>(obj: T, callback: (value: T[keyof T], key: keyof T) => any): void;

// 实现签名,处理具体逻辑
export function iterate(objOrArray: any[] | Record<string, any>, callback: (value: any, key: number | string) => any) {
    if (Array.isArray(objOrArray)) {
        objOrArray.forEach(callback);
    } else {
        for (const key in objOrArray) {
            if (objOrArray.hasOwnProperty(key)) {
                callback(objOrArray[key], key);
            }
        }
    }
}

// 测试用例正常工作
function arrayCallback(value:string, key:number){}
iterate(['a','b'],arrayCallback);

function objectCallback(value:string, key:string){}
iterate({a:'a',b:'b'},objectCallback);

方案2:使用类型断言辅助推断

如果想保留原有泛型结构,可以通过类型断言告诉TypeScript分支中的类型是安全的:

export type IterateArg = {[key:string]:any}|any[];
export type IterateKey<T extends IterateArg> = T extends any[] ? number : keyof T;

export const iterate = <Type extends IterateArg>(obj_or_array: Type, callback: (value:any,key: IterateKey<Type> )=>any) => {
    if (Array.isArray(obj_or_array)) {
        // 断言callback符合forEach的参数类型
        obj_or_array.forEach(callback as (value: any, index: number) => any);
    } else {
        for (const key in obj_or_array) {
            if (obj_or_array.hasOwnProperty(key)) {
                // 断言key符合IterateKey<Type>类型
                callback(obj_or_array[key], key as IterateKey<Type>);
            }
        }
    }
};

// 测试用例正常工作
function arrayCallback(value:string, key:number){}
iterate(['a','b'],arrayCallback);

function objectCallback(value:string, key:string){}
iterate({a:'a',b:'b'},objectCallback);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 22:25:34