Идентификационная функция с необязательным параметром - PullRequest
2 голосов
/ 14 июня 2019

Как мы можем изменить эту функцию тождественности, чтобы сделать ее параметр необязательным, чтобы она возвращала неопределенное при вызове без аргументов?

function f<T>(x: T): T {
    return x;
}

var a: number = f(1);
var b: null = f(null);
var c: undefined = f(undefined);

Очевидный подход не пропускает strictNullChecks:

function f<T>(x?: T): T {
    return x; // TS2322
}

Добавление утверждения типа, по-видимому, решает проблему…

function f<T>(x?: T): T {
    return x as T;
}

var d: undefined = f();

… но фактически открывает дверь для сбоя неправильного кода во время выполнения:

var e: number = f();
console.log(e.toExponential());

Предоставление параметра типазначение по умолчанию не помогает:

function f<T = undefined>(x?: T): T {
    return x as T;
}

1 Ответ

3 голосов
/ 14 июня 2019

Будет сложно заставить компилятор проверить, безопасна ли ваша реализация, поэтому лучшее, что я думаю, вы здесь сделаете, это то, что гарантирует, что вызывающая сторона не может вызвать его неправильно, но там, где вы используете введите assertion или «моральный эквивалент» для подавления предупреждений в реализации.

Самый простой способ сделать это - реализовать f как перегруженную функцию :

// call signatures
function f(): undefined;
function f<T>(x: T): T;
// impl signature
function f(x?: any) {
    return x;
}

Здесь вы поддерживаете две подписи вызова: либо вызов с нулевым аргументом, где результатом является undefined, и нет параметра универсального типа, либо вызов общего назначения с одним аргументом, где результат совпадает с входным. Это должно вести себя точно так, как вы ожидаете:

var a = f(1); // number
var b = f(null); // null 
var c = f(undefined); // undefined
var d = f(); // undefined
var e: number = f(); // error! undefined is not number

Перегруженная функция - это то, что я называю «морально эквивалентным» утверждению типа, потому что сигнатура реализации может быть более свободной, чем любая из сигнатур вызова. Поэтому вы должны быть осторожны, чтобы правильно реализовать эту функцию.

Если по какой-то причине вам не нужны перегрузки, вы можете использовать условные типы вместе с rest кортежами , чтобы создать обобщенную функцию, которая принимает ноль или один аргумент и тип возвращаемого значения которого равен конкретно зависит от длины списка аргументов:

function f<P extends readonly [any?]>(
  ...x: P
): P extends readonly [infer R] ? R : undefined {
  return x as any;
}

Это ведет себя так же для примеров с a по e выше. Но вы все еще делаете утверждение типа (as any в реализации), и оно более сложное и безобразное. Единственное отличие, которое я вижу, будет, если вы вызовете его с использованием аргумента типа union, например:

var hmm = f(...Math.random() < 0.5 ? ["hey"] : []) // different

, который работает для условного f, но не для перегруженного. Я сомневаюсь, что вам это нужно.

Хорошо, надеюсь, это поможет; удачи!

Ссылка на код

...