Переменные и константы в TypeScript
Дальше: Ветвления · Типы и типизация · Коллекции · Справочник — основы типизации
В runtime переменные в TypeScript — те же let и const, что в JavaScript. Отличие — контракт типа: компилятор знает, что можно присвоить, и подсказывает это в IDE. Статья логично следует за типами и типизацией; дальше — ветвления и функции.
База JS: переменные и область видимости.
null/undefinedв strict — 10.md §strictNullChecks.
const и let
| Объявление | Переназначение ссылки | Когда использовать |
|---|---|---|
const | нельзя | по умолчанию |
let | можно | счётчики, переприсваивание |
var | устарело | не использовать |
const appName = "IT Universe";
let counter = 0;
counter += 1;
// appName = "другое"; // ошибка
Разбор:
constзапрещает только переназначение ссылки (config = другойОбъект); поля объекта по-прежнему можно менять, если они неreadonly.
Вывод типов (type inference)
TypeScript выводит тип из правой части, если аннотация не указана:
const total = 100; // number
const title = "Отчёт"; // string
const flags = [true, false]; // boolean[]
let score = 10;
score = 20; // OK
// score = "текст"; // ошибка
Разбор:
- У
constпримитивов тип сужается до литерала только сas const— Типы данных и типизация в TypeScript. - У
letтип шире:number, не10.
Обязательно ли писать аннотацию типа
Нет — в большинстве случаев TypeScript выводит тип сам. Явная запись let name: type нужна, где компилятору не хватает контекста.
Когда довериться выводу, когда указать тип явно
| Ситуация | Рекомендация | Почему |
|---|---|---|
const count = 0 | довериться выводу | тип 0 или number очевиден |
const items = [] | явно : Item[] | иначе never[] |
let id; id = "a" | явно : string | без аннотации — any / ошибка |
| Локальная переменная в функции | обычно вывод | меньше шума |
| Экспортируемая функция | явно параметры и часто возврат | контракт модуля |
| Публичное поле класса | явно | API класса |
JSON / fetch | unknown + parse | вывод даст any в legacy |
| Ситуация | Аннотация при объявлении | При присвоении | При использовании |
|---|---|---|---|
const x = 10 | Не нужна (вывод number) | Тип уже зафиксирован | Проверка по выведенному типу |
let score = 10 | Не нужна | Новое значение должно быть number | То же |
let id; id = "a" | Нужна (let id: string) | Проверка совместимости с объявленным типом | — |
| Параметр функции | Обычно да (или контекст callback) | — | Аргументы проверяются при вызове |
| Возврат функции | Часто выводится; в API — явно | return проверяется | — |
| Поле объекта в литерале | Часто выводится из значения | — | Доступ к полю по типу объекта |
Аннотация при присвоении (x = value as string) — это утверждение (as), отдельный механизм от объявления типа. Предпочитайте сужение через if — ниже и в Операторы и условные ветвления в TypeScript.
Вся эта проверка выполняется на этапе TypeScript (tsc, IDE), не в runtime — Архитектура компиляции TypeScript и runtime.
Когда нужна явная аннотация
// Инициализация позже — тип обязателен
let userId: string;
userId = "u-42";
// Пустой массив без контекста → never[]
const items: string[] = [];
// Параметр функции (всегда типизируют или выводят из контекста)
function setPort(port: number): void {
console.log(port);
}
Разбор:
let x;без типа и без присваивания в strict даётany(или ошибку приnoImplicitAny) — избегайте.- Явная аннотация полезна на границах API: публичные функции, экспортируемые константы.
Аннотация и утверждение
| Конструкция | Пример | Смысл |
|---|---|---|
| Аннотация | const x: string = "a" | проверка совместимости |
| Утверждение | value as string | Доверие разработчика, проверка слабее |
Утверждение ! | el!.focus() | Разработчик обещает, что здесь не null — осторожно |
Предпочитайте аннотации и сужение типа (narrowing) через if и typeof вместо as — Операторы и условные ветвления в TypeScript.
readonly на полях и переменных
type Point = {
readonly x: number;
readonly y: number;
};
const p: Point = { x: 10, y: 20 };
// p.x = 5; // ошибка
const ids: readonly number[] = [1, 2, 3];
// ids.push(4); // ошибка
Разбор:
readonly— поверхностная защита: вложенный объект внутри поля можно менять, если не помечен.- Для глубокой неизменяемости —
Readonly<T>рекурсивно или иммутабельные обновления.
Неизменяемость и защита данных в функциях
Неизменяемость (immutability) в TypeScript — договорённость на уровне типов: функция не меняет переданные данные без ведома вызывающего. Runtime JavaScript мутацию не блокирует — readonly исчезает при компиляции, зато компилятор предупредит о нарушении контракта.
type Order = {
readonly id: string;
readonly items: readonly { sku: string; qty: number }[];
};
// Плохо: мутирует аргумент
function addItemBad(order: Order, sku: string): void {
(order.items as { sku: string; qty: number }[]).push({ sku, qty: 1 });
}
// Хорошо: возвращает новый объект
function addItem(order: Order, sku: string): Order {
return {
...order,
items: [...order.items, { sku, qty: 1 }],
};
}
| Приём | Что даёт |
|---|---|
readonly на полях | нельзя переприсвоить поле и вызвать мутирующие методы массива |
Readonly<T> / ReadonlyArray<T> | обёртка для всего объекта или массива |
Возврат нового объекта (spread) | вызывающий сохраняет старую ссылку без сюрпризов |
Readonly<User> в параметре | сигнал в API: "вход только для чтения" |
const у переменной запрещает переназначение ссылки (order = другойОбъект), но вложенный объект остаётся изменяемым. Для контракта функции типизируйте параметр как Readonly<Order> или используйте отдельный тип OrderUpdate с Partial.
Обновление состояния, объявленного через const, — это новый объект:
type Settings = {
readonly theme: "light" | "dark";
readonly locale: string;
};
function withTheme(s: Settings, theme: Settings["theme"]): Settings {
return { ...s, theme };
}
Деструктуризация
Объекты
type User = {
id: string;
name: string;
email?: string;
};
function printUser({ id, name, email }: User): void {
console.log(id, name, email ?? "—");
}
const u: User = { id: "1", name: "Анна" };
const { name: displayName } = u;
Разбор:
- Тип указывают у всего параметра
{ ... }: User, не у каждого поля отдельно. - Переименование:
{ name: displayName }.
Массивы и rest
const pair: [string, number] = ["score", 100];
const [label, value] = pair;
const [head, ...tail] = [1, 2, 3, 4];
// head: number, tail: number[]
Значения по умолчанию
function connect({
host = "localhost",
port = 3000,
}: {
host?: string;
port?: number;
}): void {
console.log(`${host}:${port}`);
}
Деструктуризация в const
const response = {
status: 200 as const,
data: { id: "1", name: "Ann" },
};
const {
data: { id, name },
} = response;
Разбор:
- Вложенная деструктуризация сохраняет вывод типов для
idиname. - При сложной форме лучше промежуточная переменная — читаемость.
strictNullChecks и переменные
С strict тип string не принимает null / undefined:
let title: string | null = null;
function render(t: string | null): string {
if (t === null) return "Без названия";
return t.toUpperCase();
}
Optional chaining при доступе к полям:
type Profile = { address?: { city?: string } };
function cityOf(p: Profile): string | undefined {
return p.address?.city;
}
Подробнее — Типы данных и типизация в TypeScript; практика проекта — Рекомендации по разработке на TypeScript.
Переменные и модули
На верхнем уровне модуля const и let не попадают в глобальный объект (в ES-модулях), в отличие от старого var в скриптах.
// utils.ts
export const API_URL = "https://api.example.com";
// main.ts
import { API_URL } from "./utils.js";
Разбор:
- Экспортируемые константы — частый способ задать контракт конфигурации с типом
as constили явным union.
Частые ошибки
| Ошибка | Причина | Что делать |
|---|---|---|
let x; без типа | неявный any | инициализировать или : T |
Менять поля const-объекта и ждать immutability | const ≠ deep freeze | readonly / новый объект |
const arr = [] | never[] | const arr: Item[] = [] |
| Деструктуризация без типа параметра | слабые подсказки | : User на параметре |
! вместо проверки | ложная уверенность | if (x !== null) |
var в циклах | утечка в замыканиях | let |
Практика
- Опишите
Settingsсreadonlyи функциюupdateSettings(s, patch)через spread. - Напишите
createOrder({ productId, qty }: { productId: string; qty: number })с деструктуризацией. - Объявите
let token: string | null = nullи функциюrequireToken()с сужением. - Создайте
const routes = ["home", "profile"] as constи типRouteчерезtypeof. - Включите
noImplicitAnyи исправьте всеletбез выведенного типа.
Смежные статьи
- Типы — примитивы, union,
as const - Синтаксис —
type,interface,import type - Ветвления — сужение типа после проверки
- Коллекции —
readonlyмассивы - Функции — параметры и rest
- Справочник — Справочник — основы типизации
- JS: Выражения и операторы в JavaScript