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'. - Что такое структурная типизация и проверка лишних свойств.