TypeScript no detecta la sobrecarga de función correcta
Dada la siguiente implementación de función:
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);
}
Obtengo los tipos correctos en los siguientes casos:
const test1 = (v: string) => fmap(v, x => x); // ✅ expected: string
const test2 = (v: string | null) => fmap(v, x => x); // ✅ expected: string | null
const test3 = (v: string | undefined) => fmap(v, x => x); // ✅ expected: string | undefined
const test4 = (v: string | null | undefined) => fmap(v, x => x); // ✅ expected: string | null | undefined
const test5 = (v: string) => fmap(v, x => 1); // ✅ expected: number
Pero no en estos casos:
// ❌ expected: number | null (actual: number)
const test6 = (v: string | null) => fmap(v, x => 1);
// ❌ expected: number | undefined (actual: number)
const test7 = (v: string | undefined) => fmap(v, x => 1);
// ❌ expected: number | null | undefined (actual: number)
const test8 = (v: string | null | undefined) => fmap(v, x => 1);
Ver patio de recreo
Respuesta #1
Necesitarás extraer los tipos anulables del tipo genérico T
type ExtractedNullable<T> = Extract<T, null | undefined>
function fmap<T, U>(value: T, f: (x: T) => U): U | ExtractedNullable<T> {
return value === undefined ? undefined as ExtractedNullable<T> : value === null ? null as ExtractedNullable<T> : f(value);
}
const test1 = (v: string) => fmap(v, x => x); // ✅ expected: string
const test2 = (v: string | null) => fmap(v, x => x); // ✅ expected: string | null
const test3 = (v: string | undefined) => fmap(v, x => x); // ✅ expected: string | undefined
const test4 = (v: string | null | undefined) => fmap(v, x => x); // ✅ expected: string | null | undefined
const test5 = (v: string) => fmap(v, x => 1); // ✅ expected: number
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
