Типы как проверка работы ИИ
пять ошибок, пойманных до запуска, и одна, которая проходит
Что это
Главная статья главы. Вернись к главе 4: там десять статей о том, как руками ловить ошибки, которых не видно при запуске. Типы делают часть этой работы механически, при каждой проверке, бесплатно.
Ниже — пять настоящих ошибок ИИ и настоящие сообщения компилятора на них. А потом — честное ограничение: одна ошибка того же рода проходит мимо, и знать про неё важнее, чем про остальные пять.
Как выглядит в коде
1. Забыл проверить пустоту
Самая частая ошибка ИИ во всей книге:
function report(c: City): string {
return `${c.name}: ${c.temp.toFixed(1)}°`;
}error TS18047: 'c.temp' is possibly 'null'.2. Перепутал имя поля
const c: City = { name: "Oslo", temperature: 4 };error TS2353: Object literal may only specify known properties,
and 'temperature' does not exist in type 'City'.В JavaScript без типов это тихо создало бы объект с лишним полем и без
нужного. Ошибка всплыла бы в другом месте и выглядела бы как undefined.
3. Перепутал порядок аргументов
function subscribe(userId: number, city: string): void {}
subscribe("Oslo", 7);error TS2345: Argument of type 'string' is not assignable
to parameter of type 'number'.Классика при работе с ИИ: он помнит функцию с похожим именем и другим порядком аргументов.
4. Забыл вариант в union
type Status = "paid" | "pending" | "refunded";
function label(s: Status): string {
switch (s) {
case "paid": return "оплачен";
case "pending": return "ожидает";
}
}error TS2366: Function lacks ending return statement
and return type does not include 'undefined'.Забыт refunded. Без типов функция молча вернула бы undefined, и в
интерфейсе появилась бы пустота вместо подписи.
Это важный случай: добавишь завтра четвёртый статус — компилятор найдёт все места, где его не обработали. Руками такое не ищут.
5. Обращение по индексу за границей
const cities = ["Oslo", "Bergen"];
const first: string = cities[5];error TS2322: Type 'string | undefined' is not assignable to type 'string'.Работает только с noUncheckedIndexedAccess (статья 9.3).
Где типы не помогают
Теперь честно. Вот код, который компилятор пропускает без единой ошибки:
interface WeatherResponse { main: { temp: number }; name: string }
async function fetchWeather(city: string): Promise<WeatherResponse> {
const raw = '{"cod":"404","message":"city not found"}';
return JSON.parse(raw) as WeatherResponse;
}
const data = await fetchWeather("Tonsberg");
console.log(data.main.temp);tsc: ошибок нетупало: Cannot read properties of undefined (reading 'temp')as WeatherResponse — это обещание компилятору, а не проверка. Ты сказал
«поверь, тут такая форма», он поверил. Пришло другое — и тот же самый
Cannot read properties of undefined, с которого начиналась глава 8.
Граница — это всё, что пришло снаружи программы:
- ответ API (
fetch,JSON.parse); - данные формы;
localStorage;- параметры из адреса страницы.
Чем закрывать границу
Библиотеками проверки во время выполнения — zod, valibot и подобными. Они
делают для фронтенда то же, что pydantic для бэкенда (статья 7.3): описываешь
форму один раз, и она и проверяет данные, и даёт тип.
Ответ API сейчас приводится через `as`. Замени на проверку
во время выполнения через zod: схема, разбор, понятная ошибка,
если форма не совпала. Тип выведи из схемы, не дублируй руками.Последняя фраза важна: смысл в том, чтобы форма описывалась один раз и служила и проверкой, и типом. Два описания рядом разъедутся.
Как сказать ИИ
Типы — ещё и способ ставить задачу. Сигнатура точнее слов (статьи 3.4, 6.5):
Напиши функцию под эту сигнатуру:
async function loadSubs(userId: number): Promise<Result<City[]>>
где Result<T> = { ok: true; data: T } | { ok: false; error: string }.
Исключения наружу не бросай, ошибки возвращай вторым вариантом.
tsc --noEmit со strict должен проходить.Проверка готового кода:
Прогони tsc --strict по своему коду мысленно: какие ошибки
он найдёт? Исправь их, не используя any, as и ts-ignore.Запрет в последней строке обязателен — иначе «исправление» будет заглушкой (статья 9.6).
Шпаргалка
Типы ловят до запуска: забытую пустоту, неверное поле, порядок аргументов, пропущенный вариант union, выход за границу массива.
Добавил вариант в union — компилятор найдёт все необработанные места.
as— обещание компилятору, а не проверка. Данные извне он не смотрит.Граница: ответ API, формы,
localStorage, адрес страницы.Границу закрывают проверкой во время выполнения, например
zod.Тип выводи из схемы, а не дублируй рядом.