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
相关产品推荐
相关产品推荐

