TypeScript无法正确匹配函数重载类型问题求助
解决TypeScript fmap函数的联合类型推断问题
先看原函数实现:
function fmap<T, U>(value: T, f: (x: T) => U): U; function fmap<T, U>(value: T | undefined, f: (x: T) => U): U | undefined; function fmap<T, U>(value: T | null, f: (x: T) => U): U | null; function fmap<T, U>(value: T | null | undefined, f: (x: T) => U): U | null | undefined { return value === undefined ? undefined : value === null ? null : f(value); }
正常工作的场景
这些调用能得到正确的类型推断:
const test1 = (v: string) => fmap(v, x => x); // ✅ 预期:string const test2 = (v: string | null) => fmap(v, x => x); // ✅ 预期:string | null const test3 = (v: string | undefined) => fmap(v, x => x); // ✅ 预期:string | undefined const test4 = (v: string | null | undefined) => fmap(v, x => x); // ✅ 预期:string | null | undefined const test5 = (v: string) => fmap(v, x => 1); // ✅ 预期:number
类型推断出错的场景
当映射函数返回固定值时,TypeScript会丢失输入中的null/undefined类型:
// ❌ 预期:number | null(实际:number) const test6 = (v: string | null) => fmap(v, x => 1); // ❌ 预期:number | undefined(实际:number) const test7 = (v: string | undefined) => fmap(v, x => 1); // ❌ 预期:number | null | undefined(实际:number) const test8 = (v: string | null | undefined) => fmap(v, x => 1);
问题原因
TypeScript的重载解析会优先匹配最具体的签名。当映射函数不依赖输入参数(比如返回固定值1)时,TypeScript会忽略输入值中的null/undefined部分,匹配第一个重载(value: T, f: (x: T) => U): U,导致返回类型丢失空值。
解决办法
改用条件类型定义返回值,替代原有的多重载写法,让TypeScript能正确跟踪联合类型中的空值:
function fmap<T, U>( value: T, f: (x: Exclude<T, null | undefined>) => U ): T extends null ? null : T extends undefined ? undefined : T extends null | undefined ? U | null | undefined : U; function fmap<T, U>(value: T, f: (x: Exclude<T, null | undefined>) => U) { if (value === undefined) return undefined as any; if (value === null) return null as any; return f(value as Exclude<T, null | undefined>); }
或者更简洁的版本,用自定义Nullable类型统一处理:
type Nullable<T> = T | null | undefined; function fmap<T, U>( value: T, f: (x: Exclude<T, null | undefined>) => U ): T extends Nullable<infer V> ? Nullable<U> : U; function fmap<T, U>(value: T, f: (x: Exclude<T, null | undefined>) => U) { return value === undefined ? undefined : value === null ? null : f(value as Exclude<T, null | undefined>); }
修改后再测试之前出错的场景,就能得到正确的类型:
const test6 = (v: string | null) => fmap(v, x => 1); // ✅ number | null const test7 = (v: string | undefined) => fmap(v, x => 1); // ✅ number | undefined const test8 = (v: string | null | undefined) => fmap(v, x => 1); // ✅ number | null | undefined
原有正常工作的场景也依然能保持正确的类型推断。
内容的提问来源于stack exchange,提问作者Eliya Cohen
相关产品推荐
相关产品推荐

