Ты освоишь механизмы сужения типов и напишешь собственные предикаты, чтобы сделать объединения надёжнее.
Открыть этот урок в KodokonСужение типов (narrowing) - это механизм, с помощью которого TypeScript уточняет широкий тип (объединение, unknown) до более точного, анализируя поток управления. Ты уже пользуешься им, не задумываясь: каждый if над типизированным значением запускает такой анализ. Компилятор знает три семейства встроенных проверок: typeof для примитивов, instanceof для экземпляров классов и in для наличия свойства. Если выбирать их с умом, тебе не придётся прибегать к рискованным приведениям через as, которые выключают проверку вместо того, чтобы направлять её.
function formatValue(
value: string | number | Date
): string {
if (typeof value === "string") {
return value.trim();
}
if (value instanceof Date) {
return value.toISOString();
}
return value.toFixed(2);
}Оператор in незаменим, когда ты работаешь со структурно разными объектами без поля-дискриминанта. Но будь внимателен: in проверяет наличие ключа, включая унаследованные, а не его тип. На внешних данных (API, JSON) он сужает тип по твоим объявлениям, а не по тому, что действительно есть во время выполнения. Классическая ловушка из практики: проверка typeof value === "object" пропускает null, потому что в JavaScript typeof null равен "object". Всегда добавляй явную проверку на null.
interface Cat { meow(): void }
interface Dog { bark(): void }
function speak(animal: Cat | Dog): void {
if ("meow" in animal) {
animal.meow();
} else {
animal.bark();
}
}Когда логика проверки становится переиспользуемой или слишком сложной для анализа потока, напиши собственный type guard: функцию, возвращаемый тип которой аннотирован как value is T. Компилятор тогда доверяет твоей реализации. Это контракт: если проверка неполная, ты возвращаешь в код ошибки времени выполнения, которых система типов уже не увидит. Начиная с TypeScript 5.5 простые предикаты выводятся автоматически в колбэках вроде filter, но для экспортируемых функций явная аннотация остаётся нормой.
interface User { id: string; email: string }
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
typeof value.id === "string" &&
"email" in value &&
typeof value.email === "string"
);
}
const raw: unknown =
JSON.parse('{"id":"1","email":"a@b.c"}');
if (isUser(raw)) {
console.log(raw.email.toLowerCase());
}typeof value === "object" недостаточно, чтобы сузить тип до объекта, отличного от null?booleanvalue as Uservalue is Userasserts value