TypeScript异步泛型函数按入参匹配Promise返回类型报TS2322
报错核心原因
这个TS2322错误是TypeScript泛型条件类型的常见特性导致的:
- 函数实现阶段,TS无法通过
Array.isArray()这类运行时类型判断,自动收窄泛型T的实际类型,进而匹配到ObjectType<T>的对应分支。当T被约束为string | string[]的联合子类型时,T extends string ? PlaylistEntity : PlaylistEntity[]是一个未完全确定的类型,TS不会认可你在分支中返回的具体PlaylistEntity或PlaylistEntity[]一定匹配这个未确定的类型,就会抛出类型不匹配错误。 - 额外的小问题:你定义的
ObjectType外层手动包裹了Promise,但async函数会自动将返回值包装为Promise,会造成返回类型推导为Promise<Promise<xxx>>,和实际逻辑不符。
最优解决方案:函数重载
这种入参类型和返回类型一一对应的场景,用函数重载是类型安全度最高、代码可读性最好的方案,不需要写强制类型断言,TS可以自动做完整的类型校验:
import { collection, query, where, getDocs, getDoc, doc } from 'firebase/firestore' import { HttpException, HttpStatus } from '@nestjs/common' // 注:PlaylistEntity、firestore为你项目中已定义/导入的内容,此处无需重复声明 // 重载签名:对外暴露的类型规则 async get(playlists: string): Promise<PlaylistEntity> async get(playlists: string[]): Promise<PlaylistEntity[]> // 实现签名:内部做逻辑实现,对外不可见 async get(playlists: string | string[]): Promise<PlaylistEntity | PlaylistEntity[]> { try { const col = collection(firestore, 'playlists') if (Array.isArray(playlists)) { const fetchArray = playlists.slice(0, 10) const q = query(col, where('__name__', 'in', fetchArray)) const docs = await getDocs(q) return docs.docs.map(v => ({ id: v.id, ...v.data() } as PlaylistEntity)) } else { const docRef = await getDoc(doc(col, playlists)) return { id: docRef.id, ...docRef.data() } as PlaylistEntity } } catch (e) { throw new HttpException((e as Error).message, HttpStatus.INTERNAL_SERVER_ERROR) } }
使用时TS会自动根据入参推导返回类型:
- 传入
string类型参数时,返回值自动推导为Promise<PlaylistEntity> - 传入
string[]类型参数时,返回值自动推导为Promise<PlaylistEntity[]>
备选方案:保留泛型+类型断言
如果你一定要保留泛型写法,可以通过类型断言手动告诉TS返回值符合类型规则,但这种方式需要你自己保证类型匹配,TS不会做额外的分支校验:
type TypeName = string | string[] // 去掉外层Promise包装,交给async函数自动处理 type ObjectType<T> = T extends string ? PlaylistEntity : PlaylistEntity[] async get<T extends TypeName>(playlists: T): Promise<ObjectType<T>> { try { const col = collection(firestore, 'playlists') if (Array.isArray(playlists)) { const fetchArray = playlists.slice(0, 10) const q = query(col, where('__name__', 'in', fetchArray)) const docs = await getDocs(q) return docs.docs.map(v => ({ id: v.id, ...v.data() } as PlaylistEntity)) as ObjectType<T> } else { const docRef = await getDoc(doc(col, playlists)) return { id: docRef.id, ...docRef.data() } as PlaylistEntity as ObjectType<T> } } catch (e) { throw new HttpException((e as Error).message, HttpStatus.INTERNAL_SERVER_ERROR) } }
提示:优先选择函数重载方案,泛型断言方案只适合重载写起来过于繁琐的复杂场景。
内容的提问来源于stack exchange,提问作者xoma_star
相关产品推荐
相关产品推荐

