Типы
Базовые и примитивные типы, кортежи, специальные типы any/unknown/never и перечисления.
15 вопросов
JuniorТеорияОчень частоЧем объединение A | B отличается от пересечения A & B?
Чем объединение A | B отличается от пересечения A & B?
A | B — значение, являющееся одним из двух, поэтому доступны лишь общие для обоих типов члены, пока вы его не сузите. A & B удовлетворяет обоим сразу, поэтому доступны члены обоих. Расширение множества значений сужает набор членов, а ограничения — расширяют.
Типичные ошибки
- ✗Менять их местами — ждать, что объединение откроет члены обоих типов
- ✗Забывать, что члены объединения недоступны, пока оно не сужено
- ✗Считать
string & numberдопустимой парой ограничений, а неnever
Уточняющие вопросы
- →Во что разрешается
string & numberи почему? - →Как добраться до члена, который есть лишь в одной ветви объединения?
MiddleТеорияОчень частоЧем отличаются any, unknown и never в TypeScript?
Чем отличаются any, unknown и never в TypeScript?
any отключает проверку типов — он присваивается во что угодно и из чего угодно, поэтому не даёт безопасности, и его лучше избегать. unknown — это безопасный аналог: любое значение присваивается ему, но перед использованием его нужно сузить через type guard. never — это пустой тип без значений; это тип возврата функции, которая всегда бросает исключение или не завершается, и тип исчерпанного объединения.
Типичные ошибки
- ✗Тянуться к
any, когдаunknownсохранил бы безопасность значения - ✗Забывать, что
unknownнужно сузить перед любым доступом к членам - ✗Считать, что
neverхранит какое-то значение, а не является пустым типом
Уточняющие вопросы
- →Почему компилятор отвергает вызов метода на значении типа
unknown? - →Где
neverпоявляется при проверке исчерпывающности?
MiddleТеорияОчень частоЧем отличаются never, void и undefined в TypeScript?
Чем отличаются never, void и undefined в TypeScript?
undefined — это настоящий тип с одним значением, undefined. void — это тип возврата функции, которая завершается, не возвращая полезного значения: вызывающий должен игнорировать результат. never — это пустой тип вообще без значений; он типизирует функцию, которая никогда не завершается нормально (всегда бросает исключение или зацикливается), и помечает исчерпанные ветки. То есть void ничего полезного не возвращает, а never не возвращается вовсе.
Типичные ошибки
- ✗Считать
voidиneverодним и тем же —voidвозвращается,neverне возвращается - ✗Думать, что
undefined— только значение, а не ещё и тип для аннотации - ✗Аннотировать бросающую или вечно зацикленную функцию как
voidвместоnever
Уточняющие вопросы
- →Почему для всегда бросающей функции выводится
never, а неvoid? - →Как
neverобеспечивает проверку исчерпывающности по объединению?
JuniorТеорияЧастоЧто такое литеральный тип и чем 'a' | 'b' отличается от string?
Что такое литеральный тип и чем 'a' | 'b' отличается от string?
У литерального типа ровно один обитатель — одно конкретное значение: 'a', 42 или true. 'a' | 'b' допускает ровно эти две строки, а string — любую. Поэтому компилятор отвергает опечатку 'c' прямо в месте вызова, ещё на этапе компиляции.
Типичные ошибки
- ✗Считать, что литеральное объединение всё равно принимает любое значение базового типа
- ✗Думать, что проверка идёт во время выполнения, а не при компиляции
- ✗Не знать, что у чисел и булей тоже есть литеральные типы
Уточняющие вопросы
- →Почему
let mode = 'a'не получает литеральный тип'a'? - →Как литеральное объединение делает
switchпроверяемым на исчерпывающесть?
JuniorТеорияЧастоЧто такое тип unknown и почему его значение нельзя использовать напрямую?
Что такое тип unknown и почему его значение нельзя использовать напрямую?
unknown — безопасный верхний тип: ему присваивается любое значение, но само оно — ничему. Его нельзя вызвать, проиндексировать или прочитать у него свойство, пока не сузите через typeof, instanceof или type guard. any разрешает всё это без проверки.
Типичные ошибки
- ✗Путать
unknownсnever—unknownпринимает любое значение,neverне принимает ни одного - ✗Брать
asвместо того, чтобы сузитьunknownчерезtypeofили type guard - ✗Считать, что
unknownразрешает обращение к свойствам так же, какany
Уточняющие вопросы
- →Как сузить
unknown, который вернулJSON.parse, до известной формы? - →Почему
unknownне присваивается никакому типу, кроме себя иany?
MiddleТеорияЧастоЧто такое enum в TypeScript и когда его использовать?
Что такое enum в TypeScript и когда его использовать?
enum задаёт именованный набор связанных констант. Числовые enum автоматически присваивают 0, 1, 2…, начиная с первого члена, а строковые enum принимают явные строковые значения. Его используют, чтобы заменить магические числа или строки читаемыми именами и сгруппировать связанные варианты, например HTTP-методы или статусы. Числовой enum создаёт runtime-объект с обратным отображением (значение → имя); строковый читается яснее в выводе, но обратного отображения не имеет.
Типичные ошибки
- ✗Считать, что числовые enum начинаются с
1— первый член по умолчанию0 - ✗Ожидать от строковых enum обратного отображения значения в имя
- ✗Забывать, что числовой enum создаёт настоящий runtime-объект, а не только типы
Уточняющие вопросы
- →Почему числовые enum дают обратное отображение, а строковые нет?
- →Когда объединение строковых литералов подойдёт лучше, чем enum?
MiddleТеорияЧастоЧем кортеж [string, number] отличается от массива (string | number)[]?
Чем кортеж [string, number] отличается от массива (string | number)[]?
Кортеж фиксирует и длину, и тип на каждой позиции: t[0] — это string, а t[1] — number. У (string | number)[] длина не фиксирована, каждый индекс типизирован объединением, и чтение требует сужения. Гарантия действует лишь при компиляции — push сработает.
Типичные ошибки
- ✗Ждать, что кортеж проконтролирует длину во время выполнения —
pushкомпилируется и работает - ✗Типизировать фиксированную пару как
(string | number)[]и потом сужать при каждом чтении - ✗Думать, что индексы кортежа типизированы объединением его элементов
Уточняющие вопросы
- →Что позволяет rest-элемент вроде
[string, ...number[]]? - →Почему
as constна литерале массива даёт readonly-кортеж?
MiddleТеорияЧастоЧто делает typeof в позиции типа и чем отличается от одноимённого оператора JS?
Что делает typeof в позиции типа и чем отличается от одноимённого оператора JS?
В позиции типа typeof x — это запрос типа: он отдаёт статический тип, выведенный компилятором для x. Он стирается и никогда не выполняется, в отличие от оператора JavaScript, возвращающего строку. Это мост от значения в мир типов: type Cfg = typeof config.
Типичные ошибки
- ✗Ждать, что
typeof xв позиции типа даст рантайм-строку'object' - ✗Считать, что запрос типа что-то выдаёт в код — он полностью стирается
- ✗Не применять
typeof, чтобы вывести тип из уже существующего конфига или константы
Уточняющие вопросы
- →Как
typeofиkeyofвместе типизируют ключи объекта? - →Почему
typeofнад объектом сas constполезнее, чем над обычным?
MiddleТеорияЧастоПочему unknown безопаснее any для значения непредсказуемой формы?
Почему unknown безопаснее any для значения непредсказуемой формы?
any отключает проверки и расползается на всё, что из него получено, поэтому ошибка всплывает лишь в рантайме. unknown принимает те же значения, но не присваивается ничему, поэтому нужна проверка typeof или type guard: ошибка ловится при проверке типов.
Типичные ошибки
- ✗Думать, что
unknownдобавляет рантайм-проверку — вся разница на этапе компиляции - ✗Присваивать
unknownпрямо в типизированную переменную, не сузив его - ✗Не замечать, что
anyрасползается на всё, что из него получено
Уточняющие вопросы
- →Что ломается, когда
anyиз нетипизированной библиотеки протекает через всю цепочку вызовов? - →Как превратить
unknownизJSON.parseв проверенный доменный тип?
JuniorТеорияИногдаКакие базовые типы доступны в TypeScript?
Какие базовые типы доступны в TypeScript?
Примитивные типы — это boolean, number (один числовой тип для целых и дробных), string, а также bigint и symbol. Коллекции представлены массивами (number[] или Array<number>) и кортежами — массивами фиксированной длины с типом на каждой позиции ([string, number]). TypeScript также добавляет enum и специальные типы any, unknown, void, never и null/undefined.
Типичные ошибки
- ✗Ожидать отдельные типы
intиfloat—numberпокрывает оба - ✗Путать кортеж с массивом — у кортежа фиксированная длина и тип на каждой позиции
- ✗Считать
any,unknownиneverзначениями времени выполнения, а не типами
Уточняющие вопросы
- →Чем тип кортежа отличается от обычного типа массива?
- →Когда стоит взять
bigintвместоnumberдля целых чисел?
MiddleТеорияИногдаЧем различаются типы object, Object и {}?
Чем различаются типы object, Object и {}?
object — любое непримитивное значение, но никогда не string и не number. Object — интерфейс Object.prototype, которому удовлетворяет почти всё, включая примитивы, поэтому он бесполезен как ограничение. {} — что угодно, кроме null и undefined.
Типичные ошибки
- ✗Брать
Objectкак ограничение и удивляться, что42ему удовлетворяет - ✗Читать
{}как пустой объект, а не как что угодно, кромеnullиundefined - ✗Считать строчный
objectи заглавныйObjectсинонимами
Уточняющие вопросы
- →Почему
const x: {} = 42компилируется? - →Что взять вместо
object, чтобы потребовать форму обычной записи?
MiddleТеорияИногдаЧем строковый enum отличается от числового enum в TypeScript?
Чем строковый enum отличается от числового enum в TypeScript?
Числовой enum нумерует члены с 0 и выдаёт обратную карту: E[0] даёт 'A'. Строковый требует явного значения у каждого члена, обратной карты не выдаёт, его значения самоописательны в рантайме. Оба выдают настоящий объект, в отличие от литерального объединения.
Типичные ошибки
- ✗Ждать обратную карту у строкового enum — её выдаёт только числовой
- ✗Думать, что enum стирается как псевдоним типа — он выдаёт настоящий объект
- ✗Рассчитывать на автоинкремент в строковом enum, где каждому члену нужно значение
Уточняющие вопросы
- →Почему строковый enum сериализуется в JSON читаемее числового?
- →Когда обычное литеральное объединение уместнее, чем enum?
SeniorТеорияИногдаЧем const enum отличается от обычного enum?
Чем const enum отличается от обычного enum?
Обычный enum компилируется в runtime-объект — лишний JavaScript, который поддерживает обратное отображение (значение → имя) и может перебираться. const enum полностью встраивается на этапе компиляции: каждое использование члена заменяется его литеральным значением, и объект не создаётся, что уменьшает вывод. Расплата — вы теряете обратное отображение и любое присутствие на рантайме, а const enum конфликтует с isolatedModules и некоторыми сборщиками, поэтому его нельзя применять там, где нужен runtime-объект.
Типичные ошибки
- ✗Думать, что
const enumвсё ещё создаёт runtime-объект — он вместо этого встраивается - ✗Ожидать обратное отображение от
const enum, которого он не даёт - ✗Применять
const enumприisolatedModulesили несовместимых сборщиках
Уточняющие вопросы
- →Почему
const enumломается приisolatedModules? - →Когда потеря обратного отображения исключает
const enum?
SeniorТеорияИногдаПочему пересечение string & number разрешается в never?
Почему пересечение string & number разрешается в never?
Пересечение допускает лишь значения, удовлетворяющие обоим ограничениям, а string и number сразу не является ничто. Это пустое множество и есть never. Компилятор сворачивает его при проверке типов; объектные пересечения обитаемы, пока не конфликтует свойство.
Типичные ошибки
- ✗Читать
neverкак тип-ошибку, а не как пустое множество значений - ✗Считать, что объектные пересечения схлопываются в
neverтак же, как примитивные - ✗Думать, что свёртка идёт во время выполнения, а не при проверке типов
Уточняющие вопросы
- →Из-за чего объектное пересечение схлопывается в
never? - →Почему
neverисчезает из объединения вродеstring | never?
SeniorКодИногдаТипизируйте spread, который дописывает элемент в кортеж и сливает два объекта
Типизируйте spread, который дописывает элемент в кортеж и сливает два объекта
Rest-элемент сохраняет позиции: тип [H, ...T] у prepend держит каждый индекс, а обычный массив даёт лишь T[]. Spread объектов перезаписывает, ровно как в рантайме: ключ, присутствующий в обоих, берёт тип второго объекта. Без any и без утверждения типа.
Типичные ошибки
- ✗Аннотировать результат prepend массивом объединения, выбрасывая все позиции
- ✗Ждать, что общий ключ возьмёт тип из первого объекта, а не из второго
- ✗Брать утверждение типа, чтобы вернуть кортеж, вместо rest-элемента в типе возврата
Уточняющие вопросы
- →Что теряется при spread обычного массива вместо кортежа?
- →Почему spread двух обобщённых параметров даёт
A & B, а не форму с перезаписью?