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

#typescript #types

#typescript #типы

Вопрос:

 type ResolverFn = () => any;

// type Notifications = () => any | number; // works as expected
type Notifications = ResolverFn | number; 
// when using the named function, ResolverFn, it fails with  error:
// "Cannot invoke an expression whose type lacks a call signature. Type 'Notifications' has no compatible call signatures."
// but it works with the unnamed () => any

type Query = {
  notifications: Notifications;
};

const Query: Query = {
  notifications() {}
};


const notifications: Notifications = () => {};

notifications();
Query.notifications();
  

Приведенный выше пример очень прост, и он вычитается из более сложного. На самом деле не существует типа, сигнатура которого является либо функцией, либо числом. Здесь это просто используется для демонстрации разницы в поведении.

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

Почему существует различное поведение с именованной и неназванной функциями? Это ожидаемое поведение? Почему? Есть ли обходной путь? Предположим, что проблема исходит из внешнего пакета, который мы не можем изменить, даже если он может быть неправильным.

any;

// type Notifications = () => any | number; // works as expected
type Notifications = ResolverFn | number;
// when using the named function, ResolverFn, it fails with error:
// «Cannot invoke an expression whose type lacks a call signature. Type ‘Notifications’ has no compatible call signatures.»
// but it works with the unnamed () => any

type Query = {
notifications: Notifications;
};

const Query: Query = {
notifications() {}
};

const notifications: Notifications = () => {};

notifications();
Query.notifications();» rel=»nofollow noreferrer»>Typescript repl

Ответ №1:

Причина, по которой встроенный тип функции «работает», заключается в том, что он не выполняет то, что вы думаете: он описывает функцию, возвращающую any | number из-за правил приоритета. Обратите внимание на разницу:

 // A function returning either any or number 
type A = () => any | number;

// Either a function returning any — or a number 
type B = (() => any) | number;
  

И причина, по которой в противном случае это не работает, заключается в том, что неясно, что notifications относится к типу функции, это также может быть число, а числа не вызываются.

Как указано в ответе @Przemyslaw Pietrzak, используйте защиту типа, чтобы сузить тип:

 if (typeof notifications === "function") {
    notifications();
} 
  

Комментарии:

1. О, facepalm был сложным. Не могли бы вы просто добавить решение ` if (typeof query.notifications === ‘function’) ` для полноты картины?

2. Готово! Я также ссылался на ответ Пшемыслава Пьетржака, поскольку он указал на это первым.

Ответ №2:

Тип уведомления о просмотре может быть либо числом, либо функцией. Итак, в последней строке TS не уверен, что Query.notifications может быть вызван safety, потому что это может быть также number, и в этом случае код выдаст ошибку во время выполнения.

Чтобы предотвратить эту ошибку, добавьте оператор if:

 if (typeof query.notifications === 'function') {
  query.notifications();
}
  

внутри if TS знает, что query.notifications это не число, поэтому его можно безопасно вызвать.