Урок по треку TypeScript 6 мин

Базовые типы TypeScript за 6 минут

Примитивы, any, unknown, never, литералы, type и interface.

TypeScript добавляет к JavaScript статические типы. Они живут только при компиляции: в выходном JS их нет, поэтому типы не могут ничего проверить во время работы программы.

Примитивы и литералы

let name: string = 'Аня';
let age: number = 30;       // целые и дробные: один тип number
let big: bigint = 10n;
let ok: boolean = true;
let id: symbol = Symbol('id');

Литеральный тип сужает примитив до одного значения: 'GET', 42, true. Объединение литералов заменяет enum во многих проектах:

type Method = 'GET' | 'POST';
let m: Method = 'GET';
m = 'PUT'; // ошибка

let расширяет литерал до общего типа (let x = 'a' даёт string), const сохраняет литерал (const x = 'a' даёт 'a'). as const делает литеральным и readonly весь объект или массив.

Вывод типов

Аннотировать всё подряд не нужно: TypeScript выводит тип из инициализатора и из return. Аннотации нужны на границах: параметры функций, публичные API, пустые массивы (const list: User[] = []).

any, unknown, never, void

  • any выключает проверку. Ему можно присвоить что угодно, и его можно присвоить чему угодно. Заразен: any.foo.bar тоже any.
  • unknown тоже принимает любое значение, но пользоваться им нельзя, пока не сузишь: if (typeof x === 'string') x.toUpperCase(). Правильный тип для JSON.parse, catch (e), данных извне.
  • never значений не имеет. Это тип функции, которая всегда бросает или зацикливается, и тип ветки, до которой невозможно дойти.
  • void значит «результат не используется». Функция () => void может вернуть что-то, но вызывающий не должен на это опираться.
function fail(msg: string): never {
  throw new Error(msg);
}

null и undefined

При strictNullChecks (входит в strict) null и undefined не входят в другие типы. string | null нужно сузить перед вызовом методов. Необязательное поле name?: string даёт тип string | undefined.

Массивы, кортежи, объекты

const ids: number[] = [1, 2];
const pair: [string, number] = ['a', 1];       // кортеж: длина и типы по позициям
const point: { x: number; y?: number } = { x: 1 };
const dict: Record<string, number> = {};        // словарь

readonly number[] запрещает push и присваивание по индексу.

type и interface

Оба описывают форму объекта. Отличия, которые спрашивают:

  • interface можно объявить повторно, объявления сольются (declaration merging). type повторно объявить нельзя.
  • type умеет объединения, кортежи, условные и mapped-типы, псевдонимы примитивов. interface описывает только объектные формы (включая функции и классы).
  • Расширение: interface B extends A против type B = A & {...}. При конфликте полей extends даёт ошибку сразу, а пересечение молча создаёт поле типа never.

Структурная типизация

TypeScript сравнивает формы, а не имена. Объект подходит, если у него есть все нужные поля нужных типов. Лишние поля разрешены, кроме проверки свежего объектного литерала:

type P = { x: number };
const a = { x: 1, y: 2 };
const p1: P = a;              // ок
const p2: P = { x: 1, y: 2 }; // ошибка: лишнее свойство в литерале

Частые ошибки

  • any там, где нужен unknown.
  • Тип Object или {} вместо object или конкретной формы: {} принимает даже строку и число.
  • String, Number с большой буквы: это обёртки, а не примитивы.
  • Ожидать, что типы проверят ответ сервера в рантайме. Для этого нужна валидация (zod, valibot).

Что спросят на собесе

  • Чем unknown отличается от any и когда что брать.
  • Что такое never и где он появляется сам.
  • Разница type и interface, что такое declaration merging.
  • Почему let x = 'a' имеет тип string, а const x = 'a' тип 'a'.
  • Что такое структурная типизация и проверка лишних свойств.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.