Учитывая следующую реализацию функции:
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); // ✅ 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
Но не в этих случаях:
// ❌ 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);
Я не понимаю, общий тип U выводится из возвращаемого типа f, поскольку f возвращает число в test6, U выводится как число, это ожидаемое значение, а не number | null.
@HairyHandKerchief23 Согласно реализации, f вызывается только в том случае, если значение не допускает значения NULL. Другими словами, test6 вернет ноль, если v не определен. Следовательно, ваше предположение неверно.
Я имею в виду, что T выводится как string | undefined вместо string, поэтому всегда используется первая перегрузка.
Я нашел способ сделать это без перегрузок: проверьте это
Впечатляющий! опубликуйте как ответ, чтобы я мог отметить его как правильный ответ.
@Silvermind, ты тоже был очень близок, тебе просто нужно было добавить дополнительную перегрузку: tsplay.dev/NDjr8W, также восстанови свой ответ, чтобы получить положительный голос.






Вам нужно будет извлечь типы, допускающие значение NULL, из универсального типа 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
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
// Should now correctly infer
const test6 = (v: string | null) => fmap(v, x => 1); // ✅ expected: number | null
const test7 = (v: string | undefined) => fmap(v, x => 1); // ✅ expected: number | undefined
const test8 = (v: string | null | undefined) => fmap(v, x => 1); // ✅ expected: number | null | undefined
где реализация?
функция fmap<T, U>(значение: T, f: (x: T) => U): U; функция fmap<T, U>(значение: T | неопределенное, f: (x: T) => U): U | неопределенный; функция fmap<T, U>(значение: T | null, f: (x: T) => U): U | нулевой; функция fmap<T, U>(значение: T | ноль | неопределенное, f: (x: T) => U): U | ноль | неопределенное {если (значение === неопределенное) {возвращение неопределенное; } Еще если (значение === null) { return null; } Еще {возврат f(значение); } }
@JollySim Вам следует отредактировать свой ответ, а не добавлять его в качестве комментария.
Удалил свой ответ. Для меня очевидно, почему первый вариант соответствует, и я думаю, что решением может быть использование
NonNullable, но я не вижу этого сразу, и у меня нет времени на дальнейшее расследование.