Перегрузки Typescript, необязательные аргументы и вывод типа - PullRequest
1 голос
/ 25 апреля 2019

В настоящее время я смотрю на перегрузки в Typescript.

Скажем, у меня есть функция с одной перегрузкой:

function method(): void;
function method(foo: boolean, bar: boolean): void;
function method(foo?: boolean, bar?: boolean) {
    if (foo === true || foo === false) {
        const result = bar;
    }
}

Либо функция вызывается без аргументов, либо она вызывается с двумя аргументами (foo и bar). Переменная result имеет тип boolean | undefined в соответствии с intellisense vscode.

Почему bar может быть undefined, хотя я проверил параметр foo? Если существует foo, не должен ли вывод типа предсказать, что bar также существует?

1 Ответ

3 голосов
/ 25 апреля 2019

Первая проблема заключается в том, что сигнатура реализации перегруженной функции может быть более свободной, чем любая из сигнатур вызова. А внутри реализации компилятор проверяет только сигнатуру реализации. Это означает, что внутри вашей функции foo и bar оба независимо от типа boolean | undefined, и нет способа восстановить тот факт, что любой, кто вызывает метод, будет указывать либо оба, либо ни то, ни другое.

TypeScript недавно добавил поддержку кортежей отдыха / распространения в параметрах функций , поэтому вы можете переписать сигнатуру функции следующим образом:

declare function method(...args: [] | [boolean, boolean]);   
method(); // okay
method(false); // errror
method(true, false); // okay

Теперь TypeScript знает, что от args до method() являются либо пустым кортежем, либо парой значений boolean. Вы можете сохранить перегрузки, если хотите, и просто сузить сигнатуру реализации:

function method(): void;
function method(foo: boolean, bar: boolean): void;
function method(...args: [] | [boolean, boolean]) {
  const foo = args[0];
  const bar = args[1];
  if (foo === true || foo === false) {
    const result = bar; // oops, still boolean | undefined
  }
}

К сожалению, вывод по-прежнему не работает, и это вторая проблема: анализ потока управления в TypeScript просто не такой умный, как мы. В то время как мы понимаем, что тип foo коррелирует с типом bar, компилятор этого не делает. Если сужается foo, но он забыл, что bar имеет какое-либо отношение к foo. Один из способов исправить это - не разбивать foo и bar на отдельные типы, а вместо этого использовать защиту типа доступа к свойствам в одной переменной args. Когда args сужается от [] | [boolean, boolean] до [boolean, boolean], вы можете быть уверены, что второй элемент определен:

function method(): void;
function method(foo: boolean, bar: boolean): void;
function method(...args: [] | [boolean, boolean]) {    
    if ('0' in args) {
        const result = args[1]; // boolean
    }
}

Это может быть слишком много изменений кода, и IntelliSense не стоит этого для вас. Если это так, и вам удобнее быть умнее компилятора, вы можете просто использовать утверждение типа и двигаться дальше со своим днем:

function method(): void;
function method(foo: boolean, bar: boolean): void;
function method(foo?: boolean, bar?: boolean) {
    if (foo === true || foo === false) {
        const result = bar as boolean; // I'm smarter than the compiler ?
    }
}

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

...