Gernar
Frontend DeveloperTypeScript

Интервью-вопрос

Что такое instanceof в TypeScript

instanceof проверяет принадлежность объекта к классу во время выполнения по цепочке прототипов. В TypeScript он полезен как type guard для классов, но не заменяет проверку интерфейсов, примитивов и plain objects из API.

Добавлен
Редакция

Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.

🐰0
🥚0

Мини-квиз

Проверка перед разбором

Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.

Вопрос 1 из 50 правильно

Что лучше сказать про роль instanceof в TypeScript?

Вы отвечаете коротко и хотите сразу отделить runtime от системы типов.

Варианты ответа

Разбор

Разобраться, а не зазубрить

Дальше разбираем суть, типичные уточнения и места, где легко сказать лишнее или перепутать термины.

Базовая идея

Короткий ответ можно начать с главного: instanceof проверяет не название типа и не набор полей. Он проверяет прототипную цепочку объекта. Справа стоит класс или функция-конструктор, слева значение. Если Constructor.prototype найден в цепочке прототипов значения, результат будет true.

Для TypeScript здесь есть два важных вывода. Оператор реально выполняется в браузере или Node.js. А еще TypeScript умеет использовать результат проверки как type guard и сужает тип внутри ветки.

class NetworkError extends Error {
  status = 500;
}

function showError(error: unknown) {
  if (error instanceof NetworkError) {
    // error сужен до NetworkError
    return `Request failed with status ${error.status}`;
  }

  if (error instanceof Error) {
    return error.message;
  }

  return "Unknown error";
}

На практике используйте instanceof, когда у вас есть реальные классы и экземпляры. Не подменяйте им проверку любых TypeScript-типов.

Что именно проверяется

В JavaScript объекты связаны с прототипами. Когда вы пишете value instanceof User, движок не сравнивает поля value с полями класса User. Он ищет User.prototype в цепочке прототипов value.

class User {
  constructor(public name: string) {}
}

const realUser = new User("Ann");
const plainUser = { name: "Ann" };

console.log(realUser instanceof User); // true
console.log(plainUser instanceof User); // false

Этот пример удобно привести на интервью. Объекты выглядят похоже по полям, но результат разный. Один создан через new User, а второй является обычным объектом.

Как выбрать проверку

Сильный ответ не заканчивается определением. Покажите, что выбираете проверку по источнику значения. Во frontend значения часто приходят из форм, API, localStorage, iframe и сторонних виджетов. Для них один и тот же инструмент не всегда подходит.

Как выбрать проверку типа

1Проверяете примитив вроде строки, числа или boolean?
Используйте typeof. instanceof здесь даст неверную интуицию и не проверит обычные примитивы.
2Проверяете реальный экземпляр класса, созданный через new?
instanceof уместен. TypeScript сузит тип внутри ветки, а runtime проверит прототипы.
3Проверяете объект из API, localStorage или JSON.parse?
Не полагайтесь на instanceof. Проверьте форму объекта или валидируйте данные явно, иначе UI может показать fallback или ошибку для корректных данных.
4У вас union из plain objects с полем kind или type?
Лучше discriminated union. Это проще читать и безопаснее для состояния UI.
5Проверяете массив, который мог прийти из iframe или другого окна?
Используйте Array.isArray, а не value instanceof Array.

Ограничение TypeScript: интерфейсы исчезают

TypeScript проверяет форму значения на этапе компиляции. Интерфейс нужен компилятору, но после сборки в JavaScript его нет. Поэтому такой код писать нельзя:

interface User {
  id: string;
  name: string;
}

function handle(value: unknown) {
  // Плохой пример: User это не runtime-значение
  if (value instanceof User) {
    console.log(value.name);
  }
}

Такой код не скомпилируется, потому что User существует только как тип. Дальше выбор зависит от модели. Для union-состояния удобно добавить поле-дискриминатор. Для данных с сервера лучше написать проверку формы или использовать валидатор.

type LoadState =
  | { kind: "idle" }
  | { kind: "success"; user: { id: string; name: string } }
  | { kind: "error"; message: string };

function render(state: LoadState) {
  if (state.kind === "success") {
    return state.user.name;
  }

  if (state.kind === "error") {
    return state.message;
  }

  return "Loading";
}

Такой вариант проще поддерживать в UI. Состояние явно описано, TypeScript хорошо сужает тип, а проверка не зависит от прототипов.

Примитивы, массивы и данные из API

Для строк, чисел и boolean обычно нужен typeof. Проверка через instanceof String или instanceof Number ищет объект-обертку, а не обычный примитив. В интерфейсе это может привести к плохому UX. Пользователь ввел нормальную строку, но код решил, что тип неверный.

const name = "Ann";

console.log(name instanceof String); // false
console.log(typeof name === "string"); // true

Для массивов часто лучше использовать Array.isArray. Это особенно полезно в браузере, где значение может прийти из другого окна или iframe. value instanceof Array привязан к конкретному конструктору Array, а Array.isArray проверяет массив надежнее.

С данными из API есть отдельная ловушка. После JSON.parse вы получаете plain object, а не экземпляр вашего класса. Даже если у объекта есть все нужные поля, payload instanceof User вернет false, пока вы явно не создадите new User(...).

class User {
  constructor(public id: string, public name: string) {}
}

const payload: unknown = JSON.parse(responseText);

// Плохой вариант: валидный ответ API уйдет в состояние ошибки,
// потому что payload не является экземпляром класса User.
if (payload instanceof User) {
  setUser(payload);
} else {
  setError("Bad response");
}

Безопаснее проверить форму ответа и отдельно решить, нужен ли вам экземпляр класса. Так UI не покажет ошибку только из-за того, что у объекта нет нужного прототипа.

function isUserPayload(value: unknown): value is { id: string; name: string } {
  return (
    typeof value === "object" &&
    value !== null &&
    "id" in value &&
    typeof value.id === "string" &&
    "name" in value &&
    typeof value.name === "string"
  );
}

if (isUserPayload(payload)) {
  setUser(new User(payload.id, payload.name));
} else {
  setError("Bad response");
}

Как звучит сильный ответ

На интервью можно сказать так:

instanceof в TypeScript это тот же оператор JavaScript. Он проверяет, есть ли prototype конструктора в цепочке прототипов объекта. TypeScript использует такую проверку как type guard и сужает тип внутри if. Я бы применял его для реальных классов, например Error или собственных классов доменной модели. Для interface, type alias, объектов из JSON и примитивов нужны другие проверки: typeof, in, discriminated union или пользовательский type guard.

Такой ответ звучит уверенно. Вы показываете границу между compile time и runtime. Еще вы не обещаете, что TypeScript проверит то, чего нет в JavaScript, и сразу называете практические альтернативы.

Практический вывод для frontend

Во frontend-коде instanceof чаще всего уместен для ошибок, классовых моделей и объектов, которые вы точно создали через new. Например, проверка error instanceof Error помогает безопасно достать message из unknown в обработчике запроса.

Но для ответов API, данных из форм и состояния React чаще лучше проверять форму данных. Иначе можно получить ветку, которая никогда не выполнится, лишний fallback, неверный текст ошибки или состояние UI, которое не соответствует реальным данным.

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

Где обычно ошибаются

Проверьте формулировки, которые звучат уверенно, но на интервью быстро выдают пробелы.

  1. 1

    Называть instanceof проверкой типов TypeScript

    instanceof работает в runtime и остается в JavaScript после компиляции. TypeScript только использует результат условия для сужения типа. Если сказать, что оператор проверяет интерфейсы TypeScript, вас легко поймают вопросом про исчезновение типов после сборки.
  2. 2

    Проверять интерфейс или type alias

    Интерфейс нельзя поставить справа от instanceof, потому что его нет в JavaScript. Для plain object лучше добавить поле-дискриминатор, например kind, или написать функцию isUser(value). Иначе код либо не скомпилируется, либо создаст ложное чувство безопасности.
  3. 3

    Использовать instanceof для обычных строк и чисел

    '42' instanceof String вернет false, потому что это примитив, а не объект-обертка. Для таких значений используйте typeof value === 'string'. Ошибка часто приводит к тому, что валидные значения из формы попадают в ветку ошибки.
  4. 4

    Доверять instanceof для данных из JSON

    Объект после JSON.parse имеет обычный прототип и не становится экземпляром вашего класса. Проверка payload instanceof User вернет false, даже если поля похожи. Нужно либо создать экземпляр явно, либо валидировать форму данных.
  5. 5

    Забывать про разные глобальные контексты

    В браузере iframe и основная страница могут иметь разные конструкторы. Из-за этого value instanceof Array может дать неожиданный результат для массива из другого окна. Для массивов лучше сказать про Array.isArray.

Follow-up

Что могут спросить дальше

Короткие ответы на вопросы, которыми проверяют понимание runtime-проверок, прототипов и границ TypeScript.

Живые ответы

Видео с похожим вопросом

Если найдем публичные интервью с таким вопросом, добавим их сюда. Их удобно смотреть после теории, чтобы свериться с живыми ответами.

Пока видео нет. Когда появятся подходящие публичные интервью, добавим их в этот блок, чтобы можно было сравнить разбор с тем, как отвечают реальные кандидаты.

Содержание