如何创建可接收值或Promise并返回同类型的TypeScript函数?
解决TypeScript中输入输出类型一致的函数定义问题
需求
创建一个TypeScript函数,满足:
- 接收类型为
A或Promise<A>的参数 - 返回类型与输入完全匹配:输入
A则返回A,输入Promise<A>则返回Promise<A>
问题分析
之前的尝试存在两个痛点:
- 使用泛型条件类型时,类型守卫分支中的参数类型变为
T & Promise<A>或T,无法正确收窄到Promise<A>或A - 使用联合类型参数
A | Promise<A>时,类型守卫能正确识别分支类型,但无法标注正确的返回类型
解决方案
方案1:函数重载
函数重载能直接定义输入输出的对应关系,类型守卫也能正确收窄分支类型:
interface A { x(): void; } function isPromise<T>(obj: unknown): obj is Promise<T> { return typeof (obj as Promise<T>).then === "function"; } const a: A = { x: () => {} }; const promiseA = Promise.resolve(a); // 重载签名:定义输入输出的对应关系 function doSomething(arg: A): A; function doSomething(arg: Promise<A>): Promise<A>; // 实现签名:处理实际逻辑 function doSomething(arg: A | Promise<A>): A | Promise<A> { if (isPromise<A>(arg)) { const type: Promise<A> = arg; // 类型正确识别为Promise<A> return Promise.resolve(a); } else { const type: A = arg; // 类型正确识别为A return a; } } // 类型检查正常通过 const anA: A = doSomething(a); const aPromiseA: Promise<A> = doSomething(promiseA); // 以下两行会触发类型错误,符合预期 // const aPromiseB: Promise<A> = doSomething(a); // const anB: A = doSomething(promiseA);
方案2:泛型条件类型优化
调整泛型约束,让TypeScript能正确推导返回类型,同时在分支中通过类型断言解决类型收窄问题:
interface A { x(): void; } function isPromise<T>(obj: unknown): obj is Promise<T> { return typeof (obj as Promise<T>).then === "function"; } const a: A = { x: () => {} }; const promiseA = Promise.resolve(a); function doSomething<T extends A | Promise<A>>(arg: T): T extends Promise<A> ? Promise<A> : A { if (isPromise<A>(arg)) { return Promise.resolve(a) as ReturnType<typeof doSomething<T>>; } else { return a as ReturnType<typeof doSomething<T>>; } } // 类型检查正常通过 const anA: A = doSomething(a); const aPromiseA: Promise<A> = doSomething(promiseA); // 以下两行会触发类型错误,符合预期 // const aPromiseB: Promise<A> = doSomething(a); // const anB: A = doSomething(promiseA);
说明
- 函数重载更直观,适合输入输出类型明确对应的场景,类型守卫在实现分支中能自动收窄类型
- 泛型条件类型更灵活,适合需要复用类型逻辑的场景,但需要显式断言返回值类型,因为TypeScript无法自动推导泛型条件类型的分支返回值
内容的提问来源于stack exchange,提问作者Joseph Earl
相关产品推荐
相关产品推荐

