Глава 2. Базовые типы: number, string, boolean, массивы

296 просмотров
0 лайков
0 в избранном

Аннотация типов

После имени переменной через двоеточие указывается тип:

const age: number = 25;
const userName: string = "Анна";
const isActive: boolean = true;

Выведение типов (type inference)

Если тип не указан, TypeScript выводит его сам по значению. Эти два варианта эквивалентны:

const age: number = 25;
const age = 25;        // TypeScript сам понял, что number

Правило: для локальных переменных тип можно не писать (выведение сработает), а для параметров функций, возвращаемых значений и публичных API — лучше указывать явно, чтобы контракт был виден другим разработчикам.

Примитивные типы

number

Все числа — и целые, и с плавающей точкой, и специальные форматы. Раздельных int/float нет:

const integer: number = 42;
const float: number = 3.14;
const hex: number = 0xff;       // 255, шестнадцатеричное
const binary: number = 0b1010;  // 10, двоичное
const octal: number = 0o744;    // 484, восьмеричное

string

Строки — в одинарных, двойных кавычках или в виде шаблонной строки (с подстановкой переменных):

const single: string = 'Привет';
const double: string = "Мир";
const name: string = "Анна";
const age: number = 28;

// Шаблонная строка — обратные кавычки и ${...}
const greeting: string = `Привет, ${name}! Тебе ${age} лет.`;

boolean

Только два значения — true и false. Часто получается как результат сравнения:

const isActive: boolean = true;
const hasAccess: boolean = age >= 18;

Массивы

Два равнозначных способа объявить массив чисел:

const numbers: number[] = [1, 2, 3];
const moreNumbers: Array<number> = [4, 5, 6];

Реальный пример — массив пользователей:

const users: string[] = ["Анна", "Борис", "Вера"];

// Методы массива сохраняют тип элемента
const upper: string[] = users.map((u) => u.toUpperCase());
const adults: string[] = users.filter((u) => u.length > 3);

Компилятор не даст положить в массив число чужого типа:

const prices: number[] = [100, 200];
prices.push("триста");
//           ^^^^^^^^
// Ошибка: аргумент типа "string" не присваивается "number"

Многомерные массивы

const matrix: number[][] = [
    [1, 2, 3],
    [4, 5, 6],
];

readonly-массивы

Если массив нельзя менять — используйте readonly:

const weekdays: readonly string[] = ["Пн", "Вт", "Ср"];
// weekdays.push("Чт"); // Ошибка: push отсутствует у readonly string[]

Кортежи (tuple)

Массив фиксированной длины с известными типами в каждой позиции. Удобен для возврата нескольких значений из функции:

const pair: [string, number] = ["Анна", 28];
const name = pair[0];   // string
const age = pair[1];    // number
// pair[2]; // Ошибка: кортеж длиной 2

Реальный кейс — результат с флагом успеха:

function tryParse(value: string): [boolean, number] {
    const parsed = Number(value);
    return [Number.isFinite(parsed) && value.trim() !== "", parsed];
}

const [ok, num] = tryParse("42");
if (ok) {
    console.log(num * 2);
}

Enum (перечисления)

Именованный набор констант. Удобен для фиксированного набора значений, например статусов заказа:

enum OrderStatus {
    New = "new",
    Paid = "paid",
    Shipped = "shipped",
    Done = "done",
}

const status: OrderStatus = OrderStatus.Paid;

if (status === OrderStatus.Paid) {
    console.log("Можно отправлять");
}

Альтернатива без enum — объединение строковых литералов (легче компилируется, рекомендуется в современных проектах):

type OrderStatus = "new" | "paid" | "shipped" | "done";
const status: OrderStatus = "paid";

any и unknown

any — отключение проверок

any — это «выключить TypeScript для этой переменной». Любая операция разрешена:

let value: any = 10;
value = "текст";        // ОК
value.toUpperCase();    // ОК для TS, но упадёт в рантайме, если value — число
value.nonexistent.prop; // ОК для TS, но упадёт

Использовать any стоит только как временную меру при миграции JS → TS. В новом коде его быть не должно.

unknown — безопасная альтернатива

unknown — «мы не знаем тип». В отличие от any, перед использованием значение нужно проверить:

let input: unknown = JSON.parse('{"x":1}');

// input.x;       // Ошибка: unknown не имеет свойств

// Безопасное использование — после проверки
if (typeof input === "object" && input !== null && "x" in input) {
    console.log("Объект с полем x");
}

Главное правило: данные «снаружи» (API, localStorage, JSON) типизируйте как unknown и проверяйте, а не как any.

null и undefined

В строгом режиме (strictNullChecks) эти типы несовместимы с другими — это защищает от частого класса ошибок:

const name: string = null;
//                ^^^^
// Ошибка: тип "null" нельзя присвоить типу "string"

// Если значение может отсутствовать — объединение:
const name: string | null = null;
const age: number | undefined = undefined;

Для безопасного доступа к свойствам есть операторы ?. (optional chaining) и ?? (nullish coalescing):

interface User {
    name: string;
    address?: { city: string };
}

const user: User | null = getUser();

// Старый способ — длинно
const city = user && user.address ? user.address.city : "неизвестно";

// Современный — короткий
const city = user?.address?.city ?? "неизвестно";

void и never

void — функция ничего не возвращает (например, console.log):

function log(message: string): void {
    console.log(message);
}

never — функция никогда не завершается нормально (бросает исключение или зацикливается):

function fail(message: string): never {
    throw new Error(message);
}

function infiniteLoop(): never {
    while (true) {}
}

Кейс из реального проекта: типизация ответа API

Представим обработку списка товаров из бэкенда:

// Без типов — любая ошибка всплывёт только в продакшене
function renderProducts(response) {
    response.products.forEach((p) => addToCart(p.id, p.price));
}
// renderProducts({ items: [] }); // тихо упадёт: products !== items

// С типами — ошибка видна сразу
interface Product {
    id: number;
    title: string;
    price: number;
}

interface ProductsResponse {
    products: Product[];
    total: number;
}

function renderProducts(response: ProductsResponse): void {
    response.products.forEach((p) => addToCart(p.id, p.price));
    //                                  ^^^^^ Тип Product, у него есть id и price
}

// renderProducts({ items: [] });
// Ошибка: отсутствует обязательное свойство "products"

Типичные ошибки

1. Указание типа там, где он и так выводится

// Лишнее
const age: number = 25;

// Достаточно
const age = 25;

2. Забыли null/undefined

function getName(): string {
    return localStorage.getItem("name");
    //     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // Ошибка: getItem возвращает string | null
}

3. Смешение типов в массиве без union

const mixed = [1, "два", true];   // (string | number | boolean)[]
mixed.push({});                    // Ошибка: объект не входит в объединение

Практика

  1. Объявите массив цен number[] и посчитайте сумму через reduce.
  2. Опишите функцию formatPrice(amount: number): string, возвращающую строку вида "100 ₽".
  3. Напишите функцию safeParse(value: unknown): number | null, которая возвращает число, если value — число или строка-число, иначе null.
  4. Объявите кортеж [string, number, boolean] для хранения пароля, длины и флага валидности.

Итог

Мы разобрали примитивные типы (number, string, boolean), массивы, кортежи, перечисления, безопасный unknown против опасного any, обработку null/undefined и служебные типы void и never. В следующей главе — как описывать форму объектов через интерфейсы и type.

Комментарии 0

Для добавления комментариев необходимо войти или зарегистрироваться.

Пока нет комментариев. Станьте первым!