Как читать чужие типы
union, сужение, дженерики, утилитные типы
Что это
Типы, которые пишет ИИ или библиотека, выглядят сложнее тех, что пишешь ты сам: угловые скобки, вертикальные черты, незнакомые слова. Читаются они проще, чем кажутся, — надо знать четыре конструкции.
Эта статья — про чтение. Писать такие типы с нуля не придётся.
Как выглядит в коде
Union и сужение
type Result =
| { ok: true; data: string[] }
| { ok: false; error: string };
function show(r: Result): string {
if (r.ok) return `получено ${r.data.length}`;
return `ошибка: ${r.error}`;
}show({ ok: true, data: ["Oslo", "Bergen"] }) -> получено 2
show({ ok: false, error: "таймаут" }) -> ошибка: таймаутЧитается так: Result — это либо объект с ok: true и data, либо объект
с ok: false и error.
Самое интересное — что происходит внутри if. После проверки r.ok
компилятор знает, какой из двух вариантов перед ним: в первой ветке есть
data и нет error, во второй наоборот. Обратишься к r.error в первой
ветке — ошибка компиляции.
Это называется сужение типа (narrowing), и это главное, ради чего union используют. Такая пара «флаг плюс данные» — стандартный способ вернуть «получилось или нет», не бросая исключение.
Сужение делают и обычные проверки:
| Проверка | Что сужает |
|---|---|
if (x === null) | убирает null из union |
typeof x === "string" | оставляет строки |
Array.isArray(x) | оставляет массивы |
if ("data" in r) | по наличию поля |
Дженерики
function firstOr<T>(items: T[], fallback: T): T {
return items.length > 0 ? items[0]! : fallback;
}firstOr(["Oslo"], "нет") -> Oslo (T = string)
firstOr([10, 20], 0) -> 10 (T = number)<T> — не конкретный тип, а заполнитель: «какой тип придёт, такой и
вернётся». Благодаря этому функция работает с любыми данными и при этом не
теряет тип: результат firstOr([10, 20], 0) — число, а не «что-то».
Читать дженерики надо как шаблон с дыркой: подставь вместо T конкретный тип
из вызова, и всё станет понятно.
Встречаются постоянно: Array<string> (то же, что string[]),
Promise<City> (промис, который вернёт City), Map<string, number>.
Утилитные типы
Готовые преобразования типов. Четыре, которые покрывают почти всё:
interface City { name: string; temp: number; country: string }
type CityPatch = Partial<City>; // все поля необязательны
type CityKey = Pick<City, "name" | "temp">;// только эти
type CityNoCountry = Omit<City, "country">; // все, кроме
type ByName = Record<string, City>; // словарь| Запись | Читается как |
|---|---|
Partial<T> | «все поля T, но необязательные» |
Pick<T, K> | «только поля K из T» |
Omit<T, K> | «все поля T, кроме K» |
Record<K, V> | «словарь: ключи K, значения V» |
Partial чаще всего встречается у функций обновления: прислать можно часть
полей. Record<string, City> — то же, что dict[str, City] в Python.
Что ещё встретится
unknown — «тип неизвестен, и пока не проверишь, ничего сделать нельзя».
Правильная замена any: заставляет проверить.
as const — «это значение не изменится», превращает "paid" из просто
строки в точное значение.
keyof City — union из имён полей: "name" | "temp" | "country".
Как сказать ИИ
Объясни этот тип по частям, как будто я вижу дженерики
впервые. Что сюда подставляется и что получается на выходе.Перепиши эту функцию так, чтобы она возвращала
{ ok: true, data } | { ok: false, error } вместо того,
чтобы бросать исключение.Здесь три почти одинаковых интерфейса. Можно ли выразить их
через Pick/Omit от одного базового? Покажи, как.На что смотреть
Union без сужения. Если тип A | B, а в коде ни одной проверки — значит,
компилятор заставили замолчать через as.
Дженерик, который везде any. <T = any> отключает всю пользу.
Типы продублированы. Три интерфейса с перекрывающимися полями вместо
Pick/Omit от одного. Разъедутся при первой правке.
unknown, которое сразу приводят через as. Смысл unknown в том, чтобы
проверить, а не в том, чтобы обойти.
Слишком умный тип. Если тип занимает десять строк и его никто не может объяснить — он сложнее задачи. ИИ иногда увлекается.
Шпаргалка
A | B— либо одно, либо другое; проверка сужает до одного.Пара
{ ok: true, data } | { ok: false, error }— стандартный «получилось или нет».<T>— заполнитель: какой тип пришёл, такой и вернётся.Partialнеобязательные,Pickтолько эти,Omitвсе кроме,Recordсловарь.unknown— честная заменаany: заставляет проверить.Не понял тип — наведи курсор, редактор покажет развёрнутый вид.