Сегодня пришло время разобрать самый фундамент, те кирпичики, из которых строится любая, даже самая сложная программа.
Сегодня мы с вами погрузимся в сердце TypeScript, в систему типов. Мы научимся явно указывать, какого рода данные мы ожидаем в нашей программе. Это похоже на инструкцию для сборки мебели. Если вы заранее знаете, что деталь A должна быть винтом, а деталь B деревянной панелью, то вы никогда не попытаетесь вкрутить панель в панель или склеить два винта. TypeScript это ваш внимательный помощник, который смотрит в эту инструкцию и не дает вам ошибиться на этапе проектирования, а не тогда, когда вся программа уже «собрана» и запущена.
Мы детально разберем три основных или примитивны, типа данных: string (строки), number (числа) и boolean (логический тип). Вы узнаете синтаксис аннотации типов, поймете, зачем нужно это явное указание и увидите, как это просто и элегантно встраивается в JavaScript-код. Мы напишем много кода, рассмотрим типичные ошибки новичков и решим несколько практических задач. Я уверен, что после этого урока вы будете чувствовать себя с типами как рыба в воде.
Что такое аннотация типа и зачем она нужна?
Давайте начнем с самого главного понятия, аннотации типа. Аннотация типа это явное указание разработчика о том, какого типа значение должно храниться в переменной, параметре функции или возвращаемом ее значении. Синтаксис аннотации очень простой: после имени идентификатора (например, названия переменной) мы ставим двоеточие и через пробел пишем название типа.
Вот как это выглядит в общем виде:
let variableName: type;
Здесь variableName это имя переменной, а type это тот самый тип, который мы хотим указать, например, string, number или boolean.
А теперь давайте ответим на главный вопрос: зачем это нужно? Ведь JavaScript прекрасно работает и без этого. Представьте себе такую ситуацию. Вы пишете функцию, которая приветствует пользователя по имени. На JavaScript это выглядело бы так:
function greet(name) { return `Hello, ${name}!`; } console.log(greet("Максим")); // Hello, Максим! console.log(greet(12345)); // Hello, 12345!
Во втором вызове мы по ошибке передали число. JavaScript не видит в этом проблемы. Он просто преобразует число в строку и честно выполнит код. В результате пользователь увидит приветствие «Hello, 12345!», что, мягко говоря, странно. Это ошибка логики и она проявится только во время выполнения программы, когда пользователь уже что-то сделал не так.
Теперь посмотрим, как эту же функцию можно написать на TypeScript:
function greet(name: string) { return `Hello, ${name}!`; } console.log(greet("Максим")); // Ок console.log(greet(12345)); // Ошибка на этапе компиляции!
В этот раз TypeScript на этапе компиляции (то есть до запуска кода) укажет нам на ошибку: «Argument of type ‘number’ is not assignable to parameter of type ‘string'». Это значит: «Аргумент типа ‘number’ не может быть присвоен параметру типа ‘string'». Мы мгновенно узнаем о проблеме, исправляем число на строку, и наша программа становится более надежной. В этом и заключается главная сила TypeScript. Он отлавливает целый класс ошибок еще до того, как код попадет в браузер или на сервер.
Аннотации типов, это документация, встроенная прямо в код. Глядя на объявление функции greet(name: string), любой разработчик, включая васшего будущего себя, сразу поймет, что функция ожидает строку. Не нужно гадать, проверять реализацию функции или читать комментарии. Типы сами все расскажут. Это делает код самодокументируемым и гораздо более легким для понимания и поддержки в больших проектах.
Тип string: работа со строками
Теперь давайте подробно познакомимся с нашим первым типом string. Строка в TypeScript (и JavaScript) это последовательность символов, используемая для представления текста. Строки можно создавать с помощью одинарных кавычек ('), двойных кавычек (") или обратных апострофов (`), которые также называются шаблонными литералами.
Объявим несколько переменных с явным указанием типа string:
let firstName: string = "Максим"; let lastName: string = 'Габов'; let message: string = `Привет, меня зовут ${firstName} ${lastName}!`; // Интерполяция
Обратите внимание на третью строку. Использование обратных апострофов позволяет нам использовать мощную функциональность, шаблонные строки (template strings). Внутри таких строк мы можем вставлять значения переменных или выражений, заключив их в ${}. Это гораздо удобнее и читабельнее, чем старый способ конкатенации (склеивания) строк через оператор +: "Привет, меня зовут " + firstName + " " + lastName + "!".
TypeScript строго следит за тем, чтобы в переменную типа string мы присваивали только строки. Давайте посмотрим на примеры с ошибками:
let myString: string; myString = "Это строка"; // OK myString = 100; // Ошибка: Type 'number' is not assignable to type 'string'. myString = true; // Ошибка: Type 'boolean' is not assignable to type 'string'.
Обе попытки присвоить число или логическое значение приведут к ошибке компиляции. TypeScript защищает нас от несоответствия типов.
Строки это неизменяемые (immutable) значения. Это значит, что любые методы работы со строками не изменяют оригинальную строку, а возвращают новую. Давайте рассмотрим некоторые базовые операции, которые вы будете делать постоянно:
let text: string = "TypeScript"; // Получение длины строки let length: number = text.length; // 10 console.log(length); // Конкатенация (склеивание) строк let hello: string = "Hello, "; let fullGreeting: string = hello + text; // "Hello, TypeScript" let fullGreetingTemplate: string = `${hello}${text}!`; // "Hello, TypeScript!" // Приведение к верхнему и нижнему регистру let upperCaseText: string = text.toUpperCase(); // "TYPESCRIPT" let lowerCaseText: string = text.toLowerCase(); // "typescript" // Получение подстроки let subString: string = text.substring(4); // "Script" (начиная с индекса 4 и до конца) let subString2: string = text.substring(0, 4); // "Type" (с индекса 0 по индекс 4, не включая его) // Поиск в строке let index: number = text.indexOf('Script'); // 4 (индекс начала подстроки 'Script')
Во всех этих операциях исходная переменная text останется равной "TypeScript". Все методы возвращают новый результат, не меняя исходник.
Явное указание типа string для переменных, которые всегда должны содержать строки. Это не только предотвращает ошибки, но и помогает таким инструментам, как IDE (Visual Studio Code, WebStorm), предоставлять вам умные подсказки (autocompletion). Начав вводить text., среда разработки сразу предложит вам все доступные методы для строк (toUpperCase, indexOf, substring и т.д.), потому что она точно знает, что text это строка.
Тип number: все числовые значения
В TypeScript, в отличие от некоторых других языков, существует всего один числовой тип number. Этот тип представляет собой число с плавающей запятой двойной точности. Это значит, что в него входят как целые числа, так и дробные. Внутри он следует стандарту IEEE 754, который используется и в JavaScript.
Давайте объявим несколько переменных типа number:
let integer: number = 42; let floatingPoint: number = 3.14159; let negative: number = -10; let scientific: number = 2.998e8; // 299,800,000 (скорость света) let binary: number = 0b1010; // 10 в двоичной системе let hexadecimal: number = 0xFF; // 255 в шестнадцатеричной системе
Как видите, TypeScript позволяет записывать числа в различных форматах для удобства. Но все они принадлежат к одному типу number.
С числовым типом можно производить все стандартные арифметические операции:
let a: number = 10; let b: number = 3; let sum: number = a + b; // 13 let difference: number = a - b; // 7 let product: number = a * b; // 30 let quotient: number = a / b; // 3.333... let remainder: number = a % b; // 1 (остаток от деления) let exponentiation: number = a ** b; // 1000 (10 в степени 3)
TypeScript так же строг с типом number, как и с string. Попытка присвоить число строке или наоборот вызовет ошибку.
let count: number = 5; count = "пять"; // Ошибка: Type 'string' is not assignable to type 'number'.
Особенно полезна эта строгость при работе с математическими операциями. В JavaScript знаменитый пример "1" + 1 даст строку "11", а "1" - 1 даст число 0. Это поведение часто приводит к трудноуловимым багам. TypeScript не даст вам совершить такую ошибку, если вы явно указали типы.
let numStr: string = "5"; let num: number = 10; // let result: number = numStr + num; // Ошибка! Нельзя склеить строку и число, если ожидается число. // let result2: string = numStr - num; // Ошибка! Нельзя вычесть число из строки, если ожидается строка. // Правильный подход, преобразовать типы явно let resultNumber: number = Number(numStr) + num; // 15 let resultString: string = numStr + String(num); // "510"
Обратите внимание на последние два примера. Иногда преобразование типов необходимо. Мы используем встроенные функции Number() и String() для явного преобразования значений в нужный нам тип. Это делает наше намерение понятным и для TypeScript и для других разработчиков.
Еще одна важная особенность это деление на ноль. В TypeScript (как и в JavaScript) это не вызывает ошибки компиляции или выполнения. instead, it returns Infinity or -Infinity.
let zero: number = 0; let infinity: number = 10 / zero; // Infinity let negativeInfinity: number = -10 / zero; // -Infinity
Работа с типом number в TypeScript интуитивно понятна, если вы знакомы с математикой. Главное преимущество, система типов ограждает вас от неявных и часто нежелательных преобразований, которые являются частым источником ошибок в JavaScript.
Тип boolean: логические значения true и false
Логический тип boolean самый простой из примитивных типов. У него может быть всего два значения: true (истина) и false (ложь). Этот тип фундаментален для реализации логики в программе: условных операторов if/else, циклов и многих других конструкций.
Объявление переменных типа boolean выглядит так:
let isReady: boolean = true; let isLoading: boolean = false;
Чаще всего значения типа boolean являются результатом логических операций или сравнений.
let a: number = 5; let b: number = 10; // Операторы сравнения возвращают boolean let isGreater: boolean = a > b; // false let isEqual: boolean = a === b; // false let isNotEqual: boolean = a !== b; // true // Логические операторы let andResult: boolean = (a < b) && (b > 0); // true (true И true = true) let orResult: boolean = (a > b) || (b < 0); // false (false ИЛИ false = false) let notResult: boolean = !(a === b); // true (НЕ false = true)
TypeScript, как и его предшественник, использует строгое сравнение (=== и !==). Оно проверяет равенство не только значений, но и типов. Это крайне важно для избежания ошибок.
let numFive: number = 5; let strFive: string = "5"; console.log(numFive == strFive); // true (в JS, нестрогое сравнение) console.log(numFive === strFive); // false (значения равны, но типы разные: number != string)
В TypeScript, если вы явно указали типы, часто даже не дойдет до использования ===, потому что компилятор не даст вам сравнить число и строку, если это не было вашим явным намерением.
let num: number = 5; let str: string = "5"; // if (num === str) { ... } // Ошибка компиляции! Это сравнение似乎 всегда будет false, т.к. типы разные.
Это еще один пример защитного механизма TypeScript. Он заставляет вас быть явным в своих намерениях. Если вы действительно хотите сравнить число и строку, вы должны сначала преобразовать один из операндов:
if (num === Number(str)) { // Явное преобразование. Теперь компилятор видит number === number // ... }
Булевы значения часто используются в условиях. Аннотация типа для параметров функции здесь особенно полезна.
function isAdult(age: number): boolean { return age >= 18; } let userAge: number = 20; if (isAdult(userAge)) { console.log("Доступ разрешен."); }
В этом примере мы аннотировали не только параметр age как number, но и указали, что функция возвращает значение типа boolean. Теперь мы можем быть уверены, что результат функции isAdult можно безопасно использовать в условном операторе if.
Помните, что для переменных типа boolean допустимы только true и false. В отличие от JavaScript, где значения like 0, "", null, undefined can be considered «falsy» in boolean context, TypeScript требует явного boolean типа where it is expected.
let isEnabled: boolean = true; // isEnabled = 1; // Ошибка: Type 'number' is not assignable to type 'boolean'.
Это предотвращает множество потенциальных ошибок, заставляя вас писать ясный и однозначный код.
Вывод типов в TypeScript
До этого момента мы везде явно указывали типы. Это отличная практика, особенно для обучения. Но TypeScript это не только про аннотации. Одна из его мощнейших приемуществ это вывод типов (Type Inference). Компилятор TypeScript невероятно умен и в большинстве случаев может самостоятельно определить тип переменной based on its value.
Взгляните на этот пример:
let message = "Hello, TypeScript!"; // TypeScript видит, что присваивается строка и понимает, что message - string let count = 42; // count выводится как number let isActive = true; // isActive выводится как boolean // message = 100; // Ошибка! Type 'number' is not assignable to type 'string'. // count = "Сорок два"; // Ошибка! Type 'string' is not assignable to type 'number'. // isActive = "yes"; // Ошибка! Type 'string' is not assignable to type 'boolean'.
Видите? Мы не написали ни одной аннотации типа для переменных, но TypeScript все равно понял, какой тип у каждой из них и продолжает защищать нас от неверных присваиваний. Это и есть вывод типов.
Зачем тогда нужны явные аннотации, если TypeScript такой умный? Есть несколько причин:
-
Ясность и документация. Аннотация типа служит документацией для вашего кода. Глядя на
let count: number = 42;, вы сразу видите намерение, чтоcountдолжен быть числом. -
Сложные случаи. Вывод типов не всегда может угадать ваш замысел, особенно когда переменная инициализируется без значения или когда тип выводится из сложной логики.
-
Параметры функций и возвращаемые значения. TypeScript часто не может вывести типы параметров функции, потому что они зависят от того, как функция будет вызвана.
Рассмотрим случай, когда вывод типов не срабатывает так, как нам хотелось бы:
let someValue; // Тип выводится как 'any' (особый тип, означающий "что угодно") someValue = "Это строка"; someValue = 100; // OK, потому что изначально тип был 'any'
Чтобы избежать нежелательного поведения типа any, лучше всегда явно аннотировать переменные, если вы не присваиваете им значение сразу:
let someValue: string; // Теперь мы явно указали, что здесь будет строка someValue = "Это строка"; // someValue = 100; // Ошибка!
Вывод типов позволяет писать менее многословный код, не жертвуя при этом безопасностью типов. Но важно знать, когда положиться на него, а когда взять управление в свои руки с помощью явных аннотаций. Со временем у вас выработается чутье на это.
Практические примеры и задачи
Давайте закрепим полученные знания на реальных примерах и небольших задачах. Попробуйте решить их самостоятельно, прежде чем смотреть решение.
Пример 1: Функция форматирования цены
Напишем функцию, которая принимает число (цену) и возвращает строку с отформатированной ценой в рублях.
function formatPrice(price: number): string { return `${price.toFixed(2)} руб.`; } let productPrice: number = 99.5; let formattedPrice: string = formatPrice(productPrice); console.log(formattedPrice); // "99.50 руб."
Объяснение: Мы аннотировали параметр price как number, потому что математические операции (like toFixed) имеют смысл только для чисел. Возвращаемый тип string, потому что шаблонная строка возвращает строку. Функция toFixed(2) округляет число до двух знаков после запятой и возвращает строку! Но так как наша функция и так должна вернуть строку, это идеально подходит.
Задача 1: Проверка пароля
Напишите функцию isStrongPassword. Она должна принимать один параметр password типа string и возвращать boolean.
Функция должна возвращать true, если длина пароля больше 8 символов и false, если нет.
Решение задачи 1:
function isStrongPassword(password: string): boolean { return password.length > 8; } console.log(isStrongPassword("qwerty")); // false console.log(isStrongPassword("securePassword123!")); // true
Пример 2: Калькулятор скидки
Создадим функцию, которая рассчитывает итоговую цену со скидкой.
function calculateDiscount(price: number, discountPercent: number): number { if (discountPercent < 0 || discountPercent > 100) { throw new Error("Скидка должна быть между 0 и 100 процентами."); } const discountAmount: number = price * (discountPercent / 100); return price - discountAmount; } let originalPrice: number = 1000; let finalPrice: number = calculateDiscount(originalPrice, 15); // 15% скидка console.log(finalPrice); // 850
Объяснение: Оба параметра числа. Мы также добавили простую проверку на валидность процента скидки. Функция возвращает итоговое число (цену).
Задача 2: Конвертер температуры
Напишите функцию celsiusToFahrenheit, которая конвертирует градусы Цельсия в градусы Фаренгейта.
Формула: Фаренгейты = (Цельсий * 9/5) + 32
Функция принимает один параметр celsius (число) и возвращает число.
Решение задачи 2:
function celsiusToFahrenheit(celsius: number): number { return (celsius * 9/5) + 32; } console.log(celsiusToFahrenheit(0)); // 32 console.log(celsiusToFahrenheit(25)); // 77 console.log(celsiusToFahrenheit(-10)); // 14
Задача 3: Поиск самого длинного слова (со звездочкой)
Напишите функцию findLongestWord. Она принимает строку, содержащую несколько слов, разделенных пробелами. Функция должна возвращать длину самого длинного слова в этой строке.
Подсказка: Вам потребуется разбить строку на массив слов с помощью метода split(' '), а затем перебрать этот массив, чтобы найти самый длинный элемент. Пока можете считать тип результата split массивом строк. Позже мы подробно изучим массивы.
Решение задачи 3:
function findLongestWord(sentence: string): number { const words: string[] = sentence.split(' '); // Разбиваем строку на массив слов let maxLength: number = 0; for (let i = 0; i < words.length; i++) { if (words[i].length > maxLength) { maxLength = words[i].length; } } return maxLength; } let longest: number = findLongestWord("TypeScript это круто и мощно"); console.log(longest); // 7 (длина слова "TypeScript")
Не переживайте, если работа с массивами показалась сложной. Мы детально разберем их в одном из следующих уроков. Главное здесь, что мы использовали тип string для входного параметра и number для возвращаемого значения.
Практикуйтесь как можно больше. Попробуйте придумать свои собственные небольшие функции, использующие string, number и boolean. Чем больше вы пишете код с явным указанием типов, тем естественнее это будет для вас.
Заключение по уроку
Вы успешно освоили фундаментальную тему в TypeScript, базовые типы и их аннотацию. Давайте кратко подведем итоги:
-
Аннотация типа это синтаксис
: type, который позволяет явно указать, значения какого типа могут храниться в переменной, параметре функции или быть ее результатом. -
Тип
stringпредназначен для текстовых данных. Он помогает избежать случайных математических операций с текстом. -
Тип
numberпредставляет как целые, так и дробные числа. Он защищает от нежелательных строковых операций с числами. -
Тип
booleanпринимает только значенияtrueилиfalse, что делает условную логику программы строгой и предсказуемой. -
Явное указание типов это мощный инструмент для документирования кода, предотвращения ошибок и улучшения поддержки кода.
-
Вывод типов явлется умной системой в TypeScript, которая во многих случаях сама определяет тип переменной, избавляя вас от необходимости писать аннотации там, где тип очевиден.
Эти знания прочный фундамент, на котором мы будем строить все последующее изучение TypeScript. В следующих уроках мы познакомимся с более сложными типами: массивами, объектами, перечислениями и типами any/unknown.
Увидимся на следующем уроке, где мы погрузимся в мир массивов и кортежей (tuples)!
Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.


