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

TypeScript中unwrap函数推断unknown而非负载类型的问题求解

问题:TypeScript 4.9中重载函数在Promise.then中类型推断异常

以下代码在TypeScript 4.9中无法编译:

interface Wrapper<T> {
    wrapped: T;
}

const p: Promise<Wrapper<string>> = Promise.resolve({ wrapped: 'foo' });

async function unwrap<T>(value: Wrapper<T>): Promise<T>;
async function unwrap<T>(value: Wrapper<T> | undefined): Promise<T | undefined>;
async function unwrap<T>(value: Wrapper<T> | undefined): Promise<T | undefined> {
    return value?.wrapped;
}

const foo: string = await p.then(unwrap);

编译器报错:

Type 'unknown' is not assignable to type 'string'.

查看.then的推断类型,发现解析值类型被推断为unknown:

Promise<Wrapper<string>>.then<unknown, string>(
  onfulfilled?: ((value: Wrapper<string>) => unknown) | null | undefined, 
  onrejected?: ((reason: any) => string | PromiseLike<string>) | null | undefined,
): Promise<...>

矛盾点在于:移除带| undefined的重载后,非空Promise场景类型检查正常,但无法处理可空Promise的场景:

const p: Promise<Wrapper<string> | undefined> = Promise.resolve(undefined);
const foo: string | undefined = p.then(unwrap);

此时报错:

Type 'Wrapper | undefined' is not assignable to type 'Wrapper'.

解决方案

方案1:使用条件类型替代重载

通过条件类型让函数自动根据输入类型推断返回值,无需重载即可同时支持非空和可空场景:

interface Wrapper<T> {
    wrapped: T;
}

async function unwrap<T>(value: T): Promise<
    T extends Wrapper<infer U> ? U :
    T extends undefined ? undefined :
    never
> {
    return value?.wrapped as any;
}

// 非空Promise场景:类型推断正确,无需空值检查
const p: Promise<Wrapper<string>> = Promise.resolve({ wrapped: 'foo' });
const foo: string = await p.then(unwrap);

// 可空Promise场景:类型自动推断为string | undefined
const p2: Promise<Wrapper<string> | undefined> = Promise.resolve(undefined);
const foo2: string | undefined = await p2.then(unwrap);

方案2:调用时显式指定泛型参数

如果坚持使用重载,在调用.then时显式指定unwrap的泛型参数,帮助TypeScript正确匹配重载:

interface Wrapper<T> {
    wrapped: T;
}

async function unwrap<T>(value: Wrapper<T>): Promise<T>;
async function unwrap<T>(value: Wrapper<T> | undefined): Promise<T | undefined>;
async function unwrap<T>(value: Wrapper<T> | undefined): Promise<T | undefined> {
    return value?.wrapped;
}

const p: Promise<Wrapper<string>> = Promise.resolve({ wrapped: 'foo' });
// 显式指定泛型参数<string>,让TypeScript匹配第一个重载
const foo: string = await p.then(unwrap<string>);

// 可空场景无需指定,自动匹配第二个重载
const p2: Promise<Wrapper<string> | undefined> = Promise.resolve(undefined);
const foo2: string | undefined = await p2.then(unwrap);

方案3:调整重载逻辑,利用泛型约束

将重载合并为单个泛型函数,通过泛型约束处理可空情况:

interface Wrapper<T> {
    wrapped: T;
}

async function unwrap<T extends Wrapper<any> | undefined>(
    value: T
): Promise<T extends Wrapper<infer U> ? U : undefined> {
    return value?.wrapped as any;
}

// 非空场景
const p: Promise<Wrapper<string>> = Promise.resolve({ wrapped: 'foo' });
const foo: string = await p.then(unwrap);

// 可空场景
const p2: Promise<Wrapper<string> | undefined> = Promise.resolve(undefined);
const foo2: string | undefined = await p2.then(unwrap);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 01:59:55