Тип возврата void и контекстная типизация

voidFunc ниже возвращает void. Однако v1 возвращает true.

Это правильно?

https://www.typescriptlang.org/docs/handbook/2/functions.html#return-type-void

type voidFunc = () => void;

const f1: voidFunc = () => {
  return true;
};

const f2: voidFunc = () => true;

const f3: voidFunc = function () {
  return true;
};

const v1 = f1();

const v2 = f2();

const v3 = f3();

console.info(v1);     //true
console.info(typeof v1);  // "boolean"
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
0
0
15
1

Ответы 1

Все идет как положено.

Похоже, вы сбиваете с толку поведение системы статических типов, наблюдаемое компилятором TypeScript и разработчиком, пишущим код TypeScript; с поведением во время выполнения, наблюдаемым механизмом JavaScript и отладкой разработчика на консоли.

Чтобы подчеркнуть разницу, вот три программы разные TypeScript, которые ведут себя идентично во время выполнения.

Я назову это "Слишком широкий":

function foo(x: string | number) {
    console.info(x.toUpperCase()); // compiler error;
    // Property 'toUpperCase' does not exist on type 'number'
}
foo("hello!");

Это "Слишком узко":

function foo(x: "goodbye!") {
    console.info(x.toUpperCase());
}
foo("hello!"); // compiler error;
// Argument of type '"hello!"' is not assignable to parameter of type '"goodbye!"'

А это «в самый раз»:

function foo(x: string) {
    console.info(x.toUpperCase());
}
foo("hello!");

Если вы скомпилируете их и запустите, вы увидите идентичные результаты. "HELLO!" войдет в консоль. Но они делают разные утверждения во время компиляции, и только "Just Right" компилятор считает правильным.

«Слишком широкий» означает, что функция foo() примет любой параметр типа string или number ... если да, то реализация foo() ошибочна, потому что она пытается получить доступ к методу toUpperCase() на чем-то, что может быть number. Кто-то мог изменить программу так, чтобы они вызывали foo(123). Любой код, который зависит от значения, переданного в foo(), является string, что-то делает неправильно.

С другой стороны, "Too Narrow" означает, что foo() будет принимать только строку буквальный"goodbye!" ... и если да, то вызов to foo("hello!") является ошибочным, потому что "hello!" не разрешен. Любой код, который передает что-то отличное от "goodbye!" в foo(), делает что-то неправильно.

Обратите внимание, что чем шире тип, тем легче предоставлять значение, которое ему удовлетворяет, но чем сложнее использовать ... с другой стороны, чем уже тип, тем легче использовать значение тот тип, но тем сложнее с предоставлять.


Итак, давайте посмотрим на функции, возвращающие void. Тип функции, такой как () => void, - очень широкий. У вас больше свободы, когда предоставление подобная функция, но мало свободы, когда она с использованием. Этот тип возвращаемого значения void означает, что «вы не должны использовать возвращаемое значение этой функции для каких-либо целей. Оно может что-то вернуть, но вы должны его игнорировать».

Например, следующая программа ошибочна:

const f: () => void = () => true;
if (f()) { console.info("hi!") }; // error!
//  ~~~ <-- An expression of type 'void' cannot be tested for truthiness.

Даже если во время выполнения вы просто проверяете if (true) {}, вполне разумное поведение, у вас плохая программа TypeScript. Вы сказали, что f() возвращает void, а это означает, что использовать это возвращаемое значение неправильно. Кто-то может изменить f(), чтобы он возвращал new Date() или undefined или что-то еще. Любой код, зависящий от boolean, делает что-то не так.

По аналогии:

const g: () => void = () => new Date();
g().getFullYear(); // error!  Property 'getFullYear' does not exist on type 'void'

Опять же, поставщик функции типа () => void может вернуть все, что пожелает; в данном случае это объект Date. Но вызывающему эту функцию нет разрешено обрабатывать возвращаемое значение, как если бы это объект Date. Это нормально во время выполнения, но это неправильная программа TypeScript. Я мог бы изменить g() так, чтобы он возвращал все, что я хочу, и тогда g().getFullYear(), вероятно, будет ошибкой во время выполнения, а также во время компиляции.


Ссылка на игровую площадку на код

Другие вопросы по теме