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

TypeScript泛型包装函数转类方法时泛型推断异常解决办法

解决TypeScript迭代器类泛型方法类型推断问题

我正尝试为TypeScript实现类似Python和Rust的迭代器,采用函数式与面向对象Iter<T>类拆分的方式。目前已实现Python内置函数和itertools的多数功能,正在迭代器类上开发。但直接重定向所有方法调用到函数式实现过于重复机械,因此尝试通过遍历generators对象的键来动态挂载原型方法,却遇到泛型推断异常:所有泛型都被推断为unknown,例如调用new Iter([1,2,3,4]).enumerate()返回的res类型为Iter<[number, unknown]>,而函数式的enumerate签名明明是function* enumerate<T>(iter: Iterator<T>): Generator<[number, T]>;调用map方法时,参数i被推断为unknown,返回类型也是Iter<unknown>。

当前代码如下:

export type Iterator<T> = IterableIterator<T> | T[];

export function* enumerate<T>(iter: Iterator<T>): Generator<[number, T]> {
    let i = 0;
    for(const item of iter) {
        yield [i, item];
        i++;
    }
}

export function* map<T, U>(iter: Iterator<T>, mapFn: (item: T) => U): Generator<U> {
    for(const item of iter) {
        yield mapFn(item);
    }
}

const generators = {enumerate, map};

type WrappedGenMethod<T, F extends (...args: any[]) => any> = 
    F extends (iter: Iterator<T>, ...args: infer Args) => Generator<infer U>
        ? (...args: Args) => Iter<U>
        : never;

/*
methods for generators
expected to be {
    enumerate: () => Iter<[number, T]>,
    map: <U>(mapFn: (item: T) => U) => Iter<U>
}
*/
type Gen<T> = {
    [key in keyof typeof generators]: WrappedGenMethod<T, typeof generators[key]>
}

export class Iter<T> implements IterableIterator<T> {
    private inner: IterableIterator<T>
    private consumed = false;

    [Symbol.iterator] = () => this.inner;

    constructor(from: Iterator<T>) {
        this.inner = from[Symbol.iterator]();
    }

    next() {
        return this.inner.next();
    }

    consume() {
        if (this.consumed) throw new Error("Iterator already consumed!");
        this.consumed = true;
    }
}

export interface Iter<T> extends Gen<T> {}

Iter.prototype.enumerate = function() {
    this.consume();
    return new Iter(generators.enumerate(this))
}

// Commented out since the compiler cannot determine T

// Iter.prototype.map = function<U>(mapFn: (item: T) => U) {
//     this.consume();
//     return new Iter(generators.map(this, mapFn))
// }

const res = new Iter([1,2,3,4]).enumerate();

问题根源

TypeScript在处理对象中泛型函数的类型映射时,typeof generators[key]会丢失原函数的泛型参数化信息,导致WrappedGenMethod无法正确关联Iter<T>的泛型T与生成器函数的泛型参数,最终推断出unknown类型。

可行解决办法

1. 改进类型映射逻辑,保留泛型参数关联

修改WrappedGenMethod的定义,通过高阶类型处理泛型函数的包装,确保Iter<T>的泛型T能传递到生成器函数中:

// 通用包装工具:移除第一个iter参数,并将返回的Generator转为Iter
type WrapGeneratorMethod<T, F> = 
    F extends (...args: [Iterator<T>, ...infer Rest]) => Generator<infer U>
        ? (...args: Rest) => Iter<U>
        : never;

// 重新定义Gen<T>
type Gen<T> = {
    [K in keyof typeof generators]: WrapGeneratorMethod<T, typeof generators[K]>
};

2. 为原型方法显式指定泛型约束

在挂载原型方法时,通过this参数约束和类型断言,强制TypeScript识别泛型参数:

// 修复enumerate方法
Iter.prototype.enumerate = function<T>(this: Iter<T>) {
    this.consume();
    return new Iter(generators.enumerate(this)) as Iter<[number, T]>;
};

// 修复map方法
Iter.prototype.map = function<T, U>(this: Iter<T>, mapFn: (item: T) => U) {
    this.consume();
    return new Iter(generators.map(this, mapFn)) as Iter<U>;
};

3. 动态挂载时自动关联泛型

如果需要批量挂载方法,可以借助类型工具批量生成正确的方法类型,再通过Object.assign挂载:

// 批量生成原型方法
const prototypeMethods = Object.fromEntries(
    Object.entries(generators).map(([key, fn]) => {
        return [key, function(...args: any[]) {
            this.consume();
            return new Iter(fn(this, ...args));
        }];
    })
) as { [K in keyof typeof generators]: Gen<unknown>[K] };

// 挂载到原型
Object.assign(Iter.prototype, prototypeMethods);

验证效果

修改后,new Iter([1,2,3,4]).enumerate()的返回类型会正确推断为Iter<[number, number]>,map方法的参数i会被推断为number,返回类型为Iter<number>。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 08:30:55