Сегодня мы разберем три очень важных типа, которые на первый взгляд могут показаться простыми, но понимание их нюансов, это признак настоящего профессионала. Мы поговорим о типах any, unknown и void.
Жизнь разработчика полна неопределенностей, данные от сервера, работа с библиотеками на JavaScript, динамический контент. В этих ситуациях типы any и unknown становятся нашими главными инструментами, но пользоваться ими нужно с умом, чтобы не свести на нет все преимущества TypeScript. А void это кирпичик для построения корректной архитектуры наших функций.
Тип any
Тип any это, пожалуй, самый мощный и в то же время самый опасный тип в TypeScript. Когда вы указываете для переменной тип any, вы буквально говорите компилятору: «Отстань! Я сам знаю, что делаю. Не проверяй эту переменную никаким образом». Это аналог динамической типизации из чистого JavaScript.
Представьте, что вы сняли все предохранители и системы безопасности с электростанции. Да, вы можете делать что угодно и очень быстро, но один неверный шаг и последствия будут катастрофическими. Именно так работает any. Он позволяет присвоить переменной значение любого типа, читать любые её свойства, вызывать её как функцию.
let dangerousVariable: any; // Всё это TypeScript пропустит без единой ошибки компиляции... dangerousVariable = 42; // число? Окей. dangerousVariable = "Hello, world!"; // строка? Без проблем. dangerousVariable = [1, 2, 3]; // массив? Да запросто! dangerousVariable.sayHello(); // Вызов несуществующего метода? И такое прокатит! dangerousVariable(); // Попытка вызвать число как функцию? Почему бы и нет! let someString: string = dangerousVariable; // Присвоить any строке? Легко!
Зачем же тогда он нужен, если он так опасен?
Ситуации бывают разные:
-
Миграция с JavaScript. Когда вы постепенно переписываете большой проект с JS на TS,
anyпомогает заглушить ошибки типизации на первых этапах, чтобы сосредоточиться на архитектуре. -
Работа с библиотеками без типов. Иногда вы используете стороннюю библиотеку, для которой нет файлов с описанием типов (
@types/package-name).anyпозволяет работать с её API, не дожидаясь, пока типы появятся. -
Динамические данные. Иногда структура данных настолько сложная и динамическая, что описать её интерфейсом практически невозможно (хотя это часто признак плохого дизайна API).
Использование any, это сознательный отказ от всех преимуществ TypeScript. Ошибки, которые компилятор мог бы поймать на этапе написания кода, проявятся только во время выполнения (runtime) и отлаживать их будет гораздо сложнее.
Практическая задача 1: Осознание опасности any
У тебя есть функция, которая принимает два числа и возвращает их сумму. Но что, если по ошибке ты передашь в нее не число?
function add(a: number, b: number): number { return a + b; } // Представь, что эти данные пришли, например, из поля ввода формы (всегда строка) let input1: any = "5"; let input2: any = 3; let result = add(input1, input2); // Компилятор не ругается! console.log(result); // "53" - конкатенация строк, а не сложение чисел!
Запусти этот код и посмотри на результат. Видишь, к чему приводит any? Ожидали число 8, а получили строку «53». TypeScript не смог нам помочь, потому что мы сказали ему «не мешай» с помощью any.
Вывод: используй any как крайнюю меру, временное решение или в исключительных ситуациях. В 95% случаев есть способ обойтись без него. И один из таких способов, его типобезопасный брат unknown.
unknown: безопасная альтернатива any
Если any это снятие всех предохранителей, то unknown это помещение переменной в защитный кейс. TypeScript говорит: «Я не знаю, что в этой коробке, и поэтому я не позволю тебе делать с этим что попало до тех пор, пока ты не докажешь мне, что именно ты собираешься делать».
Тип unknown так же всеяден, как и any. В переменную типа unknown можно положить что угодно: число, строку, массив, объект.
Ключевое отличие: с переменной типа unknown нельзя производить никаких операций, пока вы не сузите её тип.
let safeVariable: unknown; safeVariable = 42; // Можно присвоить число safeVariable = "Hello!"; // Можно присвоить строку // А вот так делать НЕЛЬЗЯ: // safeVariable.toFixed(); // Ошибка: Object is of type 'unknown' // safeVariable.toUpperCase(); // Ошибка: Object is of type 'unknown' // let someString: string = safeVariable; // Ошибка: Type 'unknown' is not assignable to type 'string' // safeVariable(); // Ошибка: Cannot invoke an object which is possibly 'unknown'
Как же тогда работать с unknown? Нужно убедить TypeScript в том, что ты знаешь, с чем имеешь дело. Это делается с помощью проверок типов (type guards).
-
Проверка типа с помощью
typeofиinstanceof:
let userData: unknown = getDataFromExternalSource(); // Предположим, функция возвращает что-то неизвестное // Мы хотим безопасно работать с userData if (typeof userData === "string") { // В этой ветке TypeScript *знает*, что userData - строка console.log(userData.toUpperCase()); // Теперь можно! } if (userData instanceof Date) { // А здесь TypeScript знает, что userData - объект Date console.log(userData.getFullYear()); // Можно вызывать методы Date! }
-
Проверка с помощью пользовательских type guards:
Это функции, которые возвращаютvalue is SomeType. Они помогают проверять сложные объектные структуры.
function isPerson(data: unknown): data is { name: string; age: number } { // Проверяем, что data - это объект и что у него есть свойства name и age нужных типов return ( typeof data === "object" && data !== null && "name" in data && "age" in data && typeof (data as any).name === "string" && typeof (data as any).age === "number" ); } let someData: unknown = { name: "Max", age: 30 }; if (isPerson(someData)) { // Теперь TypeScript знает структуру someData! console.log(`Hello, ${someData.name}. You are ${someData.age} years old.`); }
-
Приведение типа (Type Assertion):
Если ты точно уверен в типе переменной, ты можешь явно указать его компилятору. Но делай это с осторожностью!
let uncertainValue: unknown = "This is a string"; // Я уверен на 100%, что это строка, могу утвердить тип let stringLength: number = (uncertainValue as string).length; // Другой синтаксис утверждения типа (angle-bracket) let anotherWay: number = (<string>uncertainValue).length;
Практическая задача 2: Рефакторинг с any на unknown
Вспомним нашу опасную функцию add. Перепишем её, чтобы безопасно обработать входные данные.
function safeAdd(a: unknown, b: unknown): number { // Сначала проверим, что оба аргумента - числа if (typeof a === 'number' && typeof b === 'number') { return a + b; } // Если нет - вернем NaN или кинем ошибку, но это будет осознанное поведение return NaN; // Или: throw new Error('Both arguments must be numbers'); } let input1: unknown = "5"; // Данные извне let input2: unknown = 3; // Данные извне let result = safeAdd(input1, input2); // Теперь здесь будет NaN, а не сюрприз console.log(result); // NaN // А так сработает correctly: let correctResult = safeAdd(5, 3); console.log(correctResult); // 8
Видишь разницу? Теперь наша функция защищена. Мы явно обрабатываем сценарий, когда приходят данные не того типа и наш код становится предсказуемым и безопасным.
Вывод: Всегда предпочитай unknown типу any. Он заставляет задуматься о проверке типов и делает небезопасные операции явными, а значит, код становится надежнее.
void для функций, которые «ничего» не возвращают
Перейдем к более простому, но не менее важному типу void. Дословно void переводится как «пустота». Этот тип используется для указания того, что функция не возвращает никакого значения.
Важно понимать: в JavaScript функция всегда что-то возвращает. Если в ней нет оператора return или return без значения, она возвращает undefined. TypeScript абстрагируется от этого и вводит тип void, чтобы явно показать намерение разработчика: «Эта функция вызывается ради побочного эффекта, а не ради результата».
Где используется void?
-
Функции без возвращаемого значения:
function logMessage(message: string): void { console.log(message); // Здесь нет return, поэтому тип возвращаемого значения - void } function showWarning(): void { alert("This is a warning!"); // Здесь тоже нет return }
-
Методы объектов, которые изменяют внутреннее состояние:
const user = { name: "Alice", setName(newName: string): void { // Указываем, что метод не возвращает значение this.name = newName; } };
-
Обработчики событий (колбэки), где возвращаемое значение не важно:
Очень распространенный кейс.
// Предположим, у нас есть функция, которая ждет колбэк function setupEventListener(callback: (event: MouseEvent) => void) { // Эта функция будет вызывать callback, но она не ожидает от него返回值 // Ей не важно, вернет ли он что-то. Ей важен побочный эффект его выполнения. // document.addEventListener('click', callback); } // Мы можем передать в нее функцию, которая возвращает void setupEventListener((event) => { console.log(event.clientX, event.clientY); }); // Но что удивительно, мы можем передать и функцию, которая что-то возвращает! // TypeScript это разрешит, потому что он смотрит на совместимость типов. // Он считает, что функция, возвращающая число, может быть использована там, где ожидается void. // Возвращаемое значение в этом случае просто будет проигнорировано. setupEventListener((event) => { console.log("Clicked!"); return 42; // Это не вызовет ошибки, хотя мы указали тип void для callback. });
Этот момент часто вызывает путаницу. Тип void в контексте типа функции означает, что возвращаемое значение не будет использовано. Это не то же самое, что функция обязана не возвращать ничего.
void илиundefined
В TypeScript можно явно указать, что функция возвращает undefined. Но это другое.
function returnsUndefined(): undefined { console.log("I return undefined"); return; // Без return будет ошибка!必须显式返回undefined } function returnsVoid(): void { console.log("I don't care about return value"); // Здесь return не обязателен }
На практике почти всегда нужен именно void.
Практическая задача 3: Создание функций с void
Создай интерфейс для объекта «Телевизор» (TV) с двумя методами:
-
turnOn(): voidвключает телевизор (выводит сообщение в консоль). -
turnOff(): voidвыключает телевизор (выводит сообщение в консоль).
Реализуй этот интерфейс, создав объект myTV.
interface TV { turnOn(): void; turnOff(): void; } const myTV: TV = { turnOn() { console.log("TV is now on. Enjoy your show!"); }, turnOff() { console.log("TV is now off. Goodbye!"); } }; myTV.turnOn(); myTV.turnOff();
Эта задача помогает закрепить понимание, что void про намерение, а не про техническую невозможность возврата значения.
Итоги шестого урока
Сегодня мы прошли важнейший этап в понимании философии TypeScript, управление неизвестностью и побочными эффектами.
-
anyэто аварийный люк. Он полезен в крайних случаях, но его бесконтрольное использование разрушает всю систему типов и leads к ошибкам. Избегай его. -
unknownэто типобезопасный способ работы с данными, тип которых неизвестен на этапе компиляции. Он заставляет нас использовать проверки типов, делая код более надежным и предсказуемым. Выбирай его вместоanyвезде, где это возможно. -
voidэто маркер для функций, которые вызываются ради своего побочного эффекта (логирование, изменение состояния, вызов alert), а не ради возвращаемого значения.
Различие между any и unknown, это один из ключевых моментов, отличающих новичка от опытного TypeScript-разработчика. Использование unknown говорит о том, что ты думаешь о безопасности типов даже в situations с неопределенными данными.
Увидимся на следующем уроке, где мы изучим объединения (union types) и пересечения (intersection types)! Это один из моих любимых механизмов в TypeScript.
Полный курс с уроками по TypeScript для начинающих можно найти здесь: https://max-gabov.ru/typescript-dlya-nachinaushih
Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.


