Опечатка в разобранных данных спокойно компилируется под strict — найдите причину
Проект собирается с strict и noImplicitAny. В total допущена опечатка totl, а в formatOrder передают число там, где объявлен Order, — и всё же tsc молчит, а баг находят только в продакшне.
Определите, почему компилятор молчит, и назовите исправление для границы данных.
interface Order { id: string; total: number }
declare function formatOrder(order: Order): string;
async function report(url: string) {
const res = await fetch(url);
const data = await res.json();
const sum = data.totl * 1.2; // почему нет ошибки?
return formatOrder(data.total); // и здесь тоже?
}
Определите причину.
Идите по any вверх по цепочке. res.json() объявлен возвращающим any, поэтому data и всё выведенное из него — тоже any, и проверяющий перестаёт замечать опечатки и неверные аргументы. noImplicitAny не срабатывает: этот any явный.
- ✗Ждать, что
noImplicitAnyпоймаетany, явно объявленный библиотекой - ✗Аннотировать
dataкакOrderи принимать эту аннотацию за проверку в рантайме - ✗Винить выключенный
strictвместо поиска места, где вошёлany
- →Какое правило линтера показало бы этот
anyещё на ревью? - →Почему тип
unknownуdataнемедленно выводит ошибку на поверхность?
Причина
Вина не на strict и не на noImplicitAny. Оба флага включены и работают. Виноват явный any в типах самой библиотеки:
// lib.dom.d.ts
interface Body {
json(): Promise<any>; // ← вот он
}
data получает тип any. Дальше any заразен: результат каждой операции над ним — снова any.
const data = await res.json(); // any
const sum = data.totl * 1.2; // any * number → number; опечатку никто не проверяет
formatOrder(data.total); // any присваивается в Order — жалоб нет
noImplicitAny ловит только неявный any (параметр без аннотации). Здесь any объявлен явно, поэтому флаг молчит по замыслу.
Исправление
Границу типизируют как unknown и проверяют. unknown принимает те же значения, но требует сужения перед использованием — компилятор снова начинает возражать.
async function report(url: string): Promise<string> {
const res = await fetch(url);
const data: unknown = await res.json(); // ✅ проверки снова включены
if (!isOrder(data)) throw new Error('Bad order payload');
const sum = data.totl * 1.2; // ❌ error TS2339: свойства totl нет в Order
return formatOrder(data); // ✅ data сужен до Order
}
function isOrder(value: unknown): value is Order {
if (typeof value !== 'object' || value === null) return false;
const c = value as Record<string, unknown>;
return typeof c.id === 'string' && typeof c.total === 'number';
}
⚠️ await res.json() as Order не исправление: as стирается и ничего не проверяет — он лишь переносит ту же ложь с any на Order. Нужна проверка во время выполнения.