| layout | ../../layouts/Layout.astro |
|---|---|
| title | TypeScript |
| description | TypeScript, frontend fundamentals, принципы проектирования, алгоритмы, типизация и runtime-контракты |
| category | Frontend |
| kind | questions |
| order | 80 |
В чем отличие фреймворка от библиотеки (приведите примеры и отличия)?
|
Короткий ответ Библиотека решает отдельную задачу, а приложение само определяет архитектуру и момент вызова библиотеки. Примеры: RxJS, Lodash, date-fns. Полный ответ Библиотека решает отдельную задачу, а приложение само определяет архитектуру и момент вызова библиотеки. Примеры: RxJS, Lodash, date-fns. Фреймворк задает каркас приложения, жизненный цикл, правила организации кода и сам вызывает пользовательский код в нужный момент. Это называют inversion of control. Примеры: Angular, NestJS. Angular предоставляет не только рендеринг, но и DI, Router, формы, HTTP-клиент, компиляцию шаблонов, CLI и инструменты тестирования. Поэтому Angular является платформой и фреймворком, а не просто UI-библиотекой. |
Какие популярные CSS, JS библиотеки вы знаете?
|
Короткий ответ Примеры, которые уместно назвать вместе с их назначением: Полный ответ Примеры, которые уместно назвать вместе с их назначением:
На собеседовании важнее объяснить, какую проблему решает библиотека и почему она была выбрана, чем перечислить много названий. |
Что такое SOLID?
|
Короткий ответ SOLID - пять принципов проектирования: Полный ответ SOLID - пять принципов проектирования:
На практике SOLID помогает уменьшить связанность и упростить тестирование. Это ориентиры, а не требование создавать отдельный класс для каждой функции. |
Что такое DRY?
|
Короткий ответ DRY означает, что одно бизнес-правило должно иметь один источник истины. Если порог бесплатной доставки используется в нескольких местах, его лучше вынести в общую функцию или доменный сервис. Полный ответ DRY означает, что одно бизнес-правило должно иметь один источник истины. Если порог бесплатной доставки используется в нескольких местах, его лучше вынести в общую функцию или доменный сервис. Одинаковые строки кода не всегда являются дублированием: два похожих сценария можно оставить раздельными, если они меняются по разным причинам. |
Что такое KISS?
|
Короткий ответ KISS предлагает выбирать самое простое решение, которое корректно выполняет текущие требования. Полный ответ KISS предлагает выбирать самое простое решение, которое корректно выполняет текущие требования. Если обычной функции или небольшого компонента достаточно, не нужны фабрики, глубокое наследование и универсальная конфигурация. Простота не отменяет типизацию, обработку ошибок и тесты. |
Что такое YAGNI?
|
Короткий ответ YAGNI означает: не реализовывать функциональность, пока для нее нет подтвержденной потребности. Полный ответ YAGNI означает: не реализовывать функциональность, пока для нее нет подтвержденной потребности. Код «на будущее» увеличивает объем поддержки и часто основан на неверном прогнозе. При этом текущее решение должно оставаться понятным и допускать безопасные изменения. |
Что такое cohesion и coupling?
|
Короткий ответ Cohesion показывает, насколько логика внутри модуля относится к одной задаче. Coupling показывает, насколько сильно модуль зависит от деталей других модулей. Полный ответ Cohesion показывает, насколько логика внутри модуля относится к одной задаче. Coupling показывает, насколько сильно модуль зависит от деталей других модулей. Обычно стремятся к высокой cohesion и низкому явному coupling: например, состояние корзины и расчет суммы можно хранить вместе, а аналитику и HTTP вынести за ее границы. |
Что такое code smell и technical debt?
|
Короткий ответ Code smell - признак возможной проблемы дизайна: большой компонент, длинный список зависимостей, boolean-флаги с противоречивыми состояниями или повторение бизнес-правил. Полный ответ Code smell - признак возможной проблемы дизайна: большой компонент, длинный список зависимостей, boolean-флаги с противоречивыми состояниями или повторение бизнес-правил. Technical debt - будущая стоимость сделанного упрощения. Он допустим как осознанный компромисс с ограниченным риском, тестовой страховкой и планом пересмотра. |
Что такое рефакторинг?
|
Короткий ответ Рефакторинг улучшает внутреннюю структуру кода без изменения наблюдаемого поведения. Его выполняют небольшими шагами под тестами. Полный ответ Рефакторинг улучшает внутреннюю структуру кода без изменения наблюдаемого поведения. Его выполняют небольшими шагами под тестами. Если небольшое бизнес-правило нельзя проверить без большого |
Как могут конфликтовать SOLID, DRY, KISS и YAGNI?
|
Короткий ответ Принципы могут подталкивать к разным решениям: Полный ответ Принципы могут подталкивать к разным решениям:
Приоритет отдают текущим требованиям и стоимости изменений. Сначала пишут ясное рабочее решение, а абстракцию добавляют после появления устойчивого повторения или вариативности. |
Как применять инженерные принципы в Angular?
|
Короткий ответ UserFacade зависит от узкого контракта, а HTTP-реализацию можно заменить provider-ом или тестовым fake. Полный ответ
export interface UserRepository {
findById(id: string): Observable<User>;
}
export const USER_REPOSITORY = new InjectionToken<UserRepository>('USER_REPOSITORY');
@Injectable({providedIn: 'root'})
export class UserFacade {
private readonly repository = inject(USER_REPOSITORY);
load(id: string): Observable<User> {
return this.repository.findById(id);
}
}
|
Что лучше: композиция или наследование?
|
Короткий ответ Во frontend чаще выбирают композицию: компоненты, сервисы, директивы, content projection, host directives и DI можно сочетать без глубокой иерархии классов. Полный ответ Во frontend чаще выбирают композицию: компоненты, сервисы, директивы, content projection, host directives и DI можно сочетать без глубокой иерархии классов. Наследование уместно, когда существует устойчивое отношение «является» и подкласс полностью соблюдает контракт базового типа. |
Что такое функциональное программирование?
|
Короткий ответ Функциональное программирование строит вычисления вокруг функций и преобразований данных. Практические идеи: Полный ответ Функциональное программирование строит вычисления вокруг функций и преобразований данных. Практические идеи:
Во frontend это упрощает тестирование и предсказуемость состояния. Полностью избегать мутаций не обязательно: важно локализовать их на понятных границах. |
Назовите основные принципы ООП?
|
Короткий ответ ООП не требует применять наследование везде. В Angular чаще полезны композиция компонентов и сервисов, DI и небольшие интерфейсы. Полный ответ
ООП не требует применять наследование везде. В Angular чаще полезны композиция компонентов и сервисов, DI и небольшие интерфейсы. Пример сочетает четыре принципа: interface NotificationChannel {
send(message: string): void;
}
abstract class BaseNotificationChannel implements NotificationChannel {
constructor(private readonly prefix: string) {}
protected format(message: string): string {
return `${this.prefix}: ${message}`;
}
abstract send(message: string): void;
}
class EmailChannel extends BaseNotificationChannel {
send(message: string): void {
sendEmail(this.format(message));
}
}
class PushChannel extends BaseNotificationChannel {
send(message: string): void {
sendPush(this.format(message));
}
}
function notify(channel: NotificationChannel, message: string): void {
channel.send(message);
}
Отдельно часто спрашивают SOLID: пять принципов проектирования, которые помогают уменьшать связанность и делать код расширяемым и тестируемым. |
Sequence diagram / диаграмма последовательности
Что такое participant, lifeline, message и response?
|
Короткий ответ Participant - участник сценария, например Component, Service, Interceptor или Backend. Lifeline - вертикальная линия жизни участника во времени. Message - вызов или событие между участниками. Response - ответ на вызов или результат обработки. Полный ответ Participant - участник сценария, например Component, Service, Interceptor или Backend. Lifeline - вертикальная линия жизни участника во времени. Message - вызов или событие между участниками. Response - ответ на вызов или результат обработки. |
Как показать retry, timeout или fallback?
|
Короткий ответ Retry удобно показывать через loop, timeout - через отдельный error/timeout branch, fallback - как альтернативный путь после неуспешного вызова. На диаграмме должно быть видно условие перехода и кто принимает решение о повторе или fallback. Полный ответ Retry удобно показывать через sequenceDiagram
Component->>Service: load()
loop up to 3 attempts
Service->>Backend: GET /items
Backend-->>Service: timeout
end
Service-->>Component: fallback data
|
Что такое sequence diagram и для чего она нужна?
|
Короткий ответ Sequence diagram показывает, как участники системы обмениваются сообщениями во времени. Она помогает разобрать порядок вызовов, границы ответственности, async flows, ошибки и интеграции между frontend, backend и внешними сервисами. Полный ответ Sequence diagram показывает, как участники системы обмениваются сообщениями во времени. Она помогает разобрать порядок вызовов, границы ответственности, async flows, ошибки и интеграции между frontend, backend и внешними сервисами. |
Чем sequence diagram отличается от flowchart?
|
Короткий ответ Sequence diagram фокусируется на взаимодействии участников и порядке сообщений между ними. Flowchart фокусируется на ветвлениях, шагах алгоритма и принятии решений внутри процесса. Полный ответ Sequence diagram фокусируется на взаимодействии участников и порядке сообщений между ними. Flowchart фокусируется на ветвлениях, шагах алгоритма и принятии решений внутри процесса. |
Почему sequence diagram читается сверху вниз?
|
Короткий ответ Вертикальная ось показывает ход времени: верхние сообщения происходят раньше нижних. Поэтому порядок строк важен и позволяет увидеть, что произошло до запроса, после ответа, при ошибке или повторной попытке. Полный ответ Вертикальная ось показывает ход времени: верхние сообщения происходят раньше нижних. Поэтому порядок строк важен и позволяет увидеть, что произошло до запроса, после ответа, при ошибке или повторной попытке. |
Как на sequence diagram показать синхронный вызов, асинхронное сообщение и ответ?
|
Короткий ответ Обычно синхронный вызов показывают сплошной стрелкой, асинхронное сообщение - отдельной нотацией инструмента, а ответ - обратной пунктирной стрелкой. Важно не столько оформление стрелки, сколько явный порядок: кто вызывает, кого вызывает и когда получает результат. Полный ответ Обычно синхронный вызов показывают сплошной стрелкой, асинхронное сообщение - отдельной нотацией инструмента, а ответ - обратной пунктирной стрелкой. Важно не столько оформление стрелки, сколько явный порядок: кто вызывает, кого вызывает и когда получает результат. sequenceDiagram
Component->>Service: load()
Service-->>Component: data
|
Как показать альтернативные сценарии: success / error, cache hit / cache miss?
|
Короткий ответ Альтернативы показывают через блоки alt / else. В каждом блоке оставляют только сообщения, которые относятся к этому варианту, чтобы success, error, cache hit и cache miss не смешивались в одну линию. Полный ответ Альтернативы показывают через блоки sequenceDiagram
Component->>Service: load()
alt cache hit
Service-->>Component: cached data
else cache miss
Service->>Backend: GET /items
Backend-->>Service: items
end
|
Когда sequence diagram полезна frontend-разработчику?
|
Короткий ответ Она полезна при разборе авторизации, refresh token flow, загрузки данных, race conditions, optimistic updates, interceptors, guards, WebSocket-сценариев и взаимодействия нескольких сервисов. Диаграмма быстро показывает, где живет логика и какие ошибки нужно обработать. Полный ответ Она полезна при разборе авторизации, refresh token flow, загрузки данных, race conditions, optimistic updates, interceptors, guards, WebSocket-сценариев и взаимодействия нескольких сервисов. Диаграмма быстро показывает, где живет логика и какие ошибки нужно обработать. |
Какие признаки плохой или перегруженной sequence diagram?
|
Короткий ответ Слишком много участников, смешение нескольких независимых сценариев, отсутствие условий, неясные названия сообщений, детали реализации вместо бизнес-событий и попытка показать всю систему на одной диаграмме. Хорошая диаграмма отвечает на один конкретный вопрос. Полный ответ Слишком много участников, смешение нескольких независимых сценариев, отсутствие условий, неясные названия сообщений, детали реализации вместо бизнес-событий и попытка показать всю систему на одной диаграмме. Хорошая диаграмма отвечает на один конкретный вопрос. |
Нарисуйте sequence diagram для сценария refresh token в Angular.
|
Короткий ответ В ответе важно показать, что refresh выполняет interceptor, исходный запрос повторяется после получения нового access token, а component получает результат как обычный ответ сервиса. Полный ответ sequenceDiagram
participant Component
participant Service
participant Interceptor
participant Backend
Component->>Service: loadData()
Service->>Interceptor: HTTP request
Interceptor->>Backend: request with access token
Backend-->>Interceptor: 401
Interceptor->>Backend: refresh token
Backend-->>Interceptor: new access token
Interceptor->>Backend: retry original request
Backend-->>Interceptor: data
Interceptor-->>Service: data
Service-->>Component: data
В ответе важно показать, что refresh выполняет interceptor, исходный запрос повторяется после получения нового access token, а component получает результат как обычный ответ сервиса. |
Flowchart / блок-схема
Что такое flowchart и для чего она нужна?
|
Короткий ответ Flowchart, или блок-схема, показывает шаги процесса, условия и переходы между ними. Она помогает обсудить алгоритм, валидацию, бизнес-правила или decision tree без привязки к конкретному коду. Полный ответ Flowchart, или блок-схема, показывает шаги процесса, условия и переходы между ними. Она помогает обсудить алгоритм, валидацию, бизнес-правила или decision tree без привязки к конкретному коду. |
Какие базовые элементы flowchart вы знаете: start/end, process, decision, input/output?
|
Короткий ответ Start/end обозначает начало и завершение процесса. Process - действие или вычисление. Decision - условие с несколькими ветками. Input/output - получение входных данных или вывод результата. Полный ответ Start/end обозначает начало и завершение процесса. Process - действие или вычисление. Decision - условие с несколькими ветками. Input/output - получение входных данных или вывод результата. |
Как на flowchart показать условие if/else?
|
Короткий ответ Условие показывают decision-блоком с двумя или несколькими исходящими ветками. Ветки подписывают значениями условия, например yes / no, valid / invalid, success / error. Полный ответ Условие показывают decision-блоком с двумя или несколькими исходящими ветками. Ветки подписывают значениями условия,
например flowchart TD
A[Start] --> B{User authorized?}
B -->|yes| C[Show content]
B -->|no| D[Show login]
|
Как на flowchart показать цикл?
|
Короткий ответ Цикл показывают обратной связью к предыдущему шагу или условию. Важно подписать условие продолжения и условие выхода, иначе схема превращается в неясное повторение. Полный ответ Цикл показывают обратной связью к предыдущему шагу или условию. Важно подписать условие продолжения и условие выхода, иначе схема превращается в неясное повторение. flowchart TD
A[Take next item] --> B{Has item?}
B -->|yes| C[Process item]
C --> A
B -->|no| D[Finish]
|
Как flowchart помогает разобрать сложную бизнес-логику?
|
Короткий ответ Она делает явными входные данные, порядок проверок, причины отказа, крайние случаи и дублирующиеся условия. По такой схеме проще договориться с аналитиком, backend и QA до написания кода. Полный ответ Она делает явными входные данные, порядок проверок, причины отказа, крайние случаи и дублирующиеся условия. По такой схеме проще договориться с аналитиком, backend и QA до написания кода. |
Какие проблемы появляются, если пытаться описать слишком большую систему одной flowchart?
|
Короткий ответ Схема становится нечитаемой: слишком много веток, пересечений, уровней детализации и скрытых правил. Лучше разделять ее на несколько схем: общий happy path, ошибки, отдельные доменные процессы и редкие edge cases. Полный ответ Схема становится нечитаемой: слишком много веток, пересечений, уровней детализации и скрытых правил. Лучше разделять ее на несколько схем: общий happy path, ошибки, отдельные доменные процессы и редкие edge cases. |
Нарисуйте flowchart для логики показа кнопки "Купить".
|
Короткий ответ Хороший ответ показывает не только итог enabled, но и причину, которую нужно отобразить пользователю при каждом неуспешном условии. Полный ответ flowchart TD
A[Start] --> B{User authorized?}
B -->|no| C[Show reason: login required]
B -->|yes| D{Product in stock?}
D -->|no| E[Show reason: out of stock]
D -->|yes| F{User not blocked?}
F -->|no| G[Show reason: user blocked]
F -->|yes| H{Delivery available?}
H -->|no| I[Show reason: delivery unavailable]
H -->|yes| J[Show enabled Buy button]
Хороший ответ показывает не только итог |
Чем flowchart отличается от sequence diagram?
|
Короткий ответ Flowchart отвечает на вопрос "какие шаги и условия есть в процессе". Sequence diagram отвечает на вопрос "какие участники обмениваются сообщениями и в каком порядке". Полный ответ Flowchart отвечает на вопрос "какие шаги и условия есть в процессе". Sequence diagram отвечает на вопрос "какие участники обмениваются сообщениями и в каком порядке". |
Когда flowchart лучше подходит, чем sequence diagram?
|
Короткий ответ Flowchart лучше подходит для условий, алгоритмов, валидации формы, расчета статуса, показа UI-состояний и бизнес-правил, где важнее decision logic, чем обмен сообщениями между несколькими участниками. Полный ответ Flowchart лучше подходит для условий, алгоритмов, валидации формы, расчета статуса, показа UI-состояний и бизнес-правил, где важнее decision logic, чем обмен сообщениями между несколькими участниками. |
ER diagram / Entity Relationship Diagram
Что такое attribute / field?
|
Короткий ответ Attribute или field - свойство сущности: id, email, status, createdAt, price. В ER diagram поля помогают увидеть, какие данные обязательны, какие являются идентификаторами и какие участвуют в связях. Полный ответ Attribute или field - свойство сущности: |
Что такое primary key?
|
Короткий ответ Primary key - поле или набор полей, которые уникально идентифицируют запись. Для frontend это часто стабильный id, который используют в URL, normalized state, списках, track expressions и API-запросах. Полный ответ Primary key - поле или набор полей, которые уникально идентифицируют запись. Для frontend это часто стабильный |
Что такое foreign key?
|
Короткий ответ Foreign key - поле, которое ссылается на primary key другой сущности. Например, booking.userId связывает бронирование с пользователем, а session.movieId связывает сеанс с фильмом. Полный ответ Foreign key - поле, которое ссылается на primary key другой сущности. Например, |
Что такое junction table / linking table?
|
Короткий ответ Junction table, или linking table, - таблица-связка для many-to-many. Например, BookingSeat может связывать Booking и Seat, если в одном бронировании может быть несколько мест, а место участвует в разных бронированиях для разных сеансов. Полный ответ Junction table, или linking table, - таблица-связка для many-to-many. Например, |
Чем entity отличается от table?
|
Короткий ответ Entity - сущность предметной области, например User или Booking. Table - конкретное хранение этой сущности в базе данных. В простой системе они могут почти совпадать, но entity описывает смысл, а table - техническую реализацию. Полный ответ Entity - сущность предметной области, например User или Booking. Table - конкретное хранение этой сущности в базе данных. В простой системе они могут почти совпадать, но entity описывает смысл, а table - техническую реализацию. |
Что такое ER diagram и для чего она нужна?
|
Короткий ответ ER diagram показывает сущности предметной области, их поля и связи. Она помогает понять структуру данных, ограничения, отношения между объектами и то, как эти данные могут приходить во frontend через API. Полный ответ ER diagram показывает сущности предметной области, их поля и связи. Она помогает понять структуру данных, ограничения, отношения между объектами и то, как эти данные могут приходить во frontend через API. |
Какие бывают связи между сущностями: one-to-one, one-to-many, many-to-many?
|
Короткий ответ One-to-one: одной записи соответствует одна другая запись. One-to-many: одна запись связана со многими, например Movie и Sessions. Many-to-many: многие записи связаны со многими, например Users и Roles или Students и Courses. Полный ответ One-to-one: одной записи соответствует одна другая запись. One-to-many: одна запись связана со многими, например Movie и Sessions. Many-to-many: многие записи связаны со многими, например Users и Roles или Students и Courses. |
Как смоделировать many-to-many связь?
|
Короткий ответ Many-to-many обычно моделируют через отдельную связующую сущность. Она хранит ссылки на обе стороны связи и может иметь собственные поля: дату создания, статус, порядок, роль или цену. Полный ответ Many-to-many обычно моделируют через отдельную связующую сущность. Она хранит ссылки на обе стороны связи и может иметь собственные поля: дату создания, статус, порядок, роль или цену. |
Почему frontend-разработчику полезно понимать ER diagrams?
|
Короткий ответ Так проще понимать API, нормализовать данные, проектировать state, ловить неоднозначности в DTO и задавать backend правильные вопросы. ER diagram помогает увидеть, какие данные являются справочниками, какие зависят от пользователя, а какие являются результатом операции. Полный ответ Так проще понимать API, нормализовать данные, проектировать state, ловить неоднозначности в DTO и задавать backend правильные вопросы. ER diagram помогает увидеть, какие данные являются справочниками, какие зависят от пользователя, а какие являются результатом операции. |
Как ER diagram помогает понять API и структуру данных на frontend?
|
Короткий ответ Она показывает, какие объекты могут приходить вложенными, где нужны ids, какие связи надо дозагружать и какие поля нельзя редактировать напрямую. Это влияет на типы DTO, формы, кеширование, optimistic updates и normalized stores. Полный ответ Она показывает, какие объекты могут приходить вложенными, где нужны ids, какие связи надо дозагружать и какие поля нельзя редактировать напрямую. Это влияет на типы DTO, формы, кеширование, optimistic updates и normalized stores. |
Чем ER diagram отличается от class diagram?
|
Короткий ответ ER diagram описывает данные и связи предметной области. Class diagram описывает классы, методы, наследование, интерфейсы и объектную модель кода. Они могут пересекаться по названиям сущностей, но отвечают на разные вопросы. Полный ответ ER diagram описывает данные и связи предметной области. Class diagram описывает классы, методы, наследование, интерфейсы и объектную модель кода. Они могут пересекаться по названиям сущностей, но отвечают на разные вопросы. |
Нарисуйте ER diagram для простой системы бронирования билетов в кино.
|
Короткий ответ На собеседовании можно обсудить, нужна ли отдельная сущность BookingSeat, если одно бронирование содержит несколько мест или если у места есть цена на момент покупки. Полный ответ erDiagram
User ||--o{ Booking : creates
Movie ||--o{ Session : has
Session ||--o{ Seat : contains
Session ||--o{ Booking : booked_for
Booking ||--o{ Seat : reserves
Booking ||--o| Payment : paid_by
User {
string id
string email
}
Movie {
string id
string title
}
Session {
string id
string movieId
datetime startsAt
}
Seat {
string id
string sessionId
string row
string number
}
Booking {
string id
string userId
string sessionId
string status
}
Payment {
string id
string bookingId
string status
}
На собеседовании можно обсудить, нужна ли отдельная сущность |
Инструменты
Почему Mermaid удобно использовать в GitHub README?
|
Короткий ответ GitHub умеет рендерить Mermaid прямо в Markdown. Диаграмма хранится как текст рядом с документацией, ее легко читать в diff, ревьюить, менять вместе с кодом и поддерживать без отдельных бинарных файлов. Полный ответ GitHub умеет рендерить Mermaid прямо в Markdown. Диаграмма хранится как текст рядом с документацией, ее легко читать в diff, ревьюить, менять вместе с кодом и поддерживать без отдельных бинарных файлов. |
Какие инструменты можно использовать для описания диаграмм в документации: Mermaid, PlantUML, draw.io / diagrams.net?
|
Короткий ответ Mermaid и PlantUML описывают диаграммы текстом, поэтому хорошо подходят для version control и code review. draw.io / diagrams.net удобен для визуального редактирования, особенно когда нужна свободная компоновка или схема для презентации. Полный ответ Mermaid и PlantUML описывают диаграммы текстом, поэтому хорошо подходят для version control и code review. draw.io / diagrams.net удобен для визуального редактирования, особенно когда нужна свободная компоновка или схема для презентации. |
Что такое структура данных и какие виды вы знаете (Стек, etc)?
|
Короткий ответ Структура данных — способ организовать данные и операции над ними. Полный ответ Структура данных — способ организовать данные и операции над ними.
На собеседовании полезно сравнить сложность основных операций по времени и памяти, а не только дать определения. |
Чем Θ-нотация отличается от O-нотации?
|
Короткий ответ O(f(n)) задает верхнюю границу роста, а Θ(f(n)) - точную асимптотическую оценку сверху и снизу. Полный ответ
Например, полный проход по массиву всегда выполняет пропорциональное |
Как оптимизировать перебор двумя циклами?
|
Короткий ответ Часто один набор заранее индексируют через Map или Set, заменяя повторный линейный поиск на lookup: Полный ответ Часто один набор заранее индексируют через const usersById = new Map(users.map((user) => [user.id, user]));
const ordersWithUsers = orders.map((order) => ({
...order,
user: usersById.get(order.userId),
}));Вместо примерно |
Чем линейный поиск отличается от бинарного?
|
Короткий ответ Линейный поиск проверяет элементы последовательно и работает за O(n). Он подходит для небольшого или неотсортированного списка. Полный ответ Линейный поиск проверяет элементы последовательно и работает за Бинарный поиск делит область поиска пополам и работает за |
Какой алгоритм сортировки полезно знать?
|
Короткий ответ Merge sort делит массив пополам, сортирует части и сливает их. Временная сложность - O(n log n), дополнительная память - O(n). Полный ответ Merge sort делит массив пополам, сортирует части и сливает их. Временная сложность - В прикладном frontend-коде обычно используют встроенный |
Как следить за чистотой кода?
|
Короткий ответ Помогают небольшие функции, точные имена, строгие типы, явные состояния, отсутствие скрытых side effects и регулярное удаление мертвого кода. Полный ответ Помогают небольшие функции, точные имена, строгие типы, явные состояния, отсутствие скрытых side effects и регулярное удаление мертвого кода. ESLint, Prettier и тесты автоматизируют проверки, но не заменяют ясные границы модулей и ответственность разработчика. |
Что такое сложность алгоритмов?
|
Короткий ответ Big O показывает верхнюю асимптотическую границу роста времени или памяти при увеличении входных данных. Константы и небольшие слагаемые обычно отбрасывают. Полный ответ Big O показывает верхнюю асимптотическую границу роста времени или памяти при увеличении входных данных. Константы и небольшие слагаемые обычно отбрасывают.
Один цикл по |
Как измерять сложность алгоритма?
|
Короткий ответ Определяют размер входа n, считают наиболее часто выполняемые операции и оставляют доминирующий член. Полный ответ Определяют размер входа Помимо времени учитывают память, реальные размеры данных и стоимость операций. Профилирование дополняет асимптотическую оценку: алгоритм с лучшим Big O не всегда быстрее на маленьком входе. |
Какова сложность доступа в основных структурах данных?
|
Короткий ответ В JavaScript для очереди частый shift() массива имеет O(n). Для большой очереди лучше использовать индекс начала или специализированную структуру. Полный ответ
В JavaScript для очереди частый |
Что такое дерево и где деревья встречаются в Angular-приложении?
|
Короткий ответ Дерево — иерархическая структура из узлов, где у узла может быть родитель и дочерние узлы. Во frontend деревья встречаются постоянно: DOM tree, accessibility tree, component tree, Angular Router tree, DI tree, Reactive Forms tree, AST в TypeScript/ESLint/Babel. Полный ответ Дерево — иерархическая структура из узлов, где у узла может быть родитель и дочерние узлы. Во frontend деревья встречаются постоянно: DOM tree, accessibility tree, component tree, Angular Router tree, DI tree, Reactive Forms tree, AST в TypeScript/ESLint/Babel. Обход дерева нужен для поиска узла, валидации вложенной формы, построения меню, анализа AST или сериализации состояния. DFS удобно идет вглубь ветки, BFS обходит уровень за уровнем. function collectInvalidControls(control: AbstractControl): AbstractControl[] {
const invalidControls: AbstractControl[] = [];
const stack: AbstractControl[] = [control];
while (stack.length > 0) {
const current = stack.pop();
if (current === undefined) {
continue;
}
if (current.invalid) {
invalidControls.push(current);
}
if ('controls' in current) {
stack.push(...Object.values(current.controls));
}
}
return invalidControls;
} |
Что такое граф и где графы встречаются во frontend-разработке?
|
Короткий ответ Граф состоит из вершин и связей между ними. Directed graph имеет направленные ребра, undirected graph — связи без направления. Граф может храниться как adjacency list или adjacency matrix. Полный ответ Граф состоит из вершин и связей между ними. Directed graph имеет направленные ребра, undirected graph — связи без направления. Граф может храниться как adjacency list или adjacency matrix. Во frontend графы встречаются в routing flows, state machines, dependency graph bundler-а, Nx affected graph, module federation dependencies, диаграммах, permission models и формах с зависимыми полями. Cycle в dependency graph часто означает архитектурную проблему: модули начинают зависеть друг от друга, сборка и тесты становятся хрупкими. DFS помогает находить cycle, BFS — искать кратчайший путь по числу переходов в простом графе. |
Что такое NP-complete и нужно ли это frontend-разработчику?
|
Короткий ответ NP-complete — класс задач, для которых неизвестен быстрый алгоритм общего вида, а перебор вариантов быстро становится непрактичным. Для frontend это редко повседневная тема, но идея полезна при обсуждении scheduling, layout, оптимизации маршрутов, упаковки элементов и сложных combinatorial constraints. Полный ответ NP-complete — класс задач, для которых неизвестен быстрый алгоритм общего вида, а перебор вариантов быстро становится непрактичным. Для frontend это редко повседневная тема, но идея полезна при обсуждении scheduling, layout, оптимизации маршрутов, упаковки элементов и сложных combinatorial constraints. На интервью достаточно объяснить практический вывод: если точное решение слишком дорогое, выбирают ограничения, эвристики, приближенный алгоритм, предварительный расчет на сервере или упрощение продукта. |
Почему Array.includes внутри filter может случайно дать O(n²)?
|
Короткий ответ includes() ищет элемент линейно. Если вызвать его для каждого элемента другого массива, получится вложенный перебор. На маленьком списке это нормально, но на тысячах строк таблицы может стать bottleneck. Полный ответ
const selectedUsers = users.filter((user) => selectedIds.includes(user.id));Если const selectedIdSet = new Set(selectedIds);
const selectedUsers = users.filter((user) => selectedIdSet.has(user.id));Так поиск по выбранным id становится в среднем |
Как выбрать между Array, Object, Map и Set?
|
Короткий ответ Для UI-состояния структура должна выражать сценарий. Если список нужен для отображения, часто оставляют Array. Если нужны частые проверки выбранности, рядом хранят Set id. Если нужно быстро получить сущность по id, добавляют Map. Полный ответ
Для UI-состояния структура должна выражать сценарий. Если список нужен для отображения, часто оставляют |
Что значит stable sort и почему это важно для UI?
|
Короткий ответ Stable sort сохраняет относительный порядок элементов, которые считаются равными comparator-ом. Это важно для таблиц: если пользователь сначала отсортировал список по имени, а потом по роли, элементы с одинаковой ролью могут сохранить предыдущий порядок по имени. Полный ответ Stable sort сохраняет относительный порядок элементов, которые считаются равными comparator-ом. Это важно для таблиц: если пользователь сначала отсортировал список по имени, а потом по роли, элементы с одинаковой ролью могут сохранить предыдущий порядок по имени. Современный JavaScript требует стабильную сортировку const sorted = users.toSorted((first, second) => {
const byRole = first.role.localeCompare(second.role);
return byRole === 0 ? first.name.localeCompare(second.name) : byRole;
}); |
Чем отличаются preorder, inorder, postorder, DFS и BFS?
|
Короткий ответ DFS идет в глубину. Preorder обрабатывает узел до детей, postorder — после детей, inorder применим прежде всего к бинарным деревьям и обходит левую ветку, узел, правую ветку. Полный ответ DFS идет в глубину. Preorder обрабатывает узел до детей, postorder — после детей, inorder применим прежде всего к бинарным деревьям и обходит левую ветку, узел, правую ветку. BFS идет по уровням и обычно использует очередь. Он удобен, когда нужно найти ближайший подходящий узел или обработать иерархию слоями. В Angular-практике preorder похож на обработку route/config tree сверху вниз, postorder удобен для агрегации валидности детей формы, а BFS может пригодиться для поиска ближайшего видимого пункта в древовидном UI. |
Что такое BST и почему он не всегда дает O(log n)?
|
Короткий ответ Binary Search Tree хранит меньшие значения в левом поддереве, большие — в правом. Если дерево сбалансировано, поиск, вставка и удаление работают за O(log n). Полный ответ Binary Search Tree хранит меньшие значения в левом поддереве, большие — в правом. Если дерево сбалансировано, поиск,
вставка и удаление работают за Если добавлять уже отсортированные значения в обычный BST без балансировки, дерево может выродиться в цепочку, и поиск
станет Frontend-разработчику редко нужно писать BST руками. Важнее понимать идею упорядоченной структуры и почему обычный
массив с бинарным поиском, |
Чем adjacency list отличается от adjacency matrix?
|
Короткий ответ Adjacency list хранит для каждой вершины список соседей. Он обычно экономнее для sparse graph, где связей мало относительно числа возможных связей. Полный ответ Adjacency list хранит для каждой вершины список соседей. Он обычно экономнее для sparse graph, где связей мало относительно числа возможных связей. Adjacency matrix хранит таблицу const dependencies = new Map<string, readonly string[]>([
['checkout', ['shared-ui', 'payments']],
['payments', ['shared-ui']],
]);Для большинства frontend-задач adjacency list через |
Когда рекурсию лучше заменить итерацией во frontend-коде?
|
Короткий ответ Рекурсия хорошо выражает дерево или вложенную структуру, но глубина call stack ограничена. Если данные могут прийти от API и быть очень глубокими, рекурсивный обход рискует упасть с RangeError. Полный ответ Рекурсия хорошо выражает дерево или вложенную структуру, но глубина call stack ограничена. Если данные могут прийти от
API и быть очень глубокими, рекурсивный обход рискует упасть с Итерация с явным stack или queue безопаснее для больших структур и позволяет проще разбивать работу на chunks, чтобы не блокировать main thread. const stack: TreeNode[] = [root];
while (stack.length > 0) {
const node = stack.pop();
if (node === undefined) {
continue;
}
processNode(node);
stack.push(...node.children);
} |
Что такое bitwise operations и почему в JavaScript с ними нужно быть осторожным?
|
Короткий ответ Bitwise operations работают с битовым представлением числа: &, |, ^, , >, >>>. В JavaScript такие операции приводят number к 32-bit integer, поэтому большие значения, дроби и NaN ведут себя не как обычная арифметика number. Полный ответ Bitwise operations работают с битовым представлением числа: Битовые маски могут пригодиться для компактного набора flags, permissions или низкоуровневой работы с binary data. Но в
обычном frontend-бизнес-коде читаемый объект, const enum Permission {
Read = 1 << 0,
Write = 1 << 1,
}
const canWrite = (permissions: number): boolean => (permissions & Permission.Write) !== 0; |
Зачем frontend-разработчику понимать Big O?
|
Короткий ответ Big O помогает оценить, как растет стоимость операции при увеличении входных данных. Для frontend это не абстрактная математика: фильтрация таблицы, группировка списка, сортировка на клиенте, поиск выбранных элементов и построение дерева меню могут выполняться на main thread и напрямую влиять на отзывчивость UI. Полный ответ Big O помогает оценить, как растет стоимость операции при увеличении входных данных. Для frontend это не абстрактная математика: фильтрация таблицы, группировка списка, сортировка на клиенте, поиск выбранных элементов и построение дерева меню могут выполняться на main thread и напрямую влиять на отзывчивость UI. Важно помнить, что Big O не заменяет профилирование. |
Как оценить сложность фильтрации, сортировки и группировки списка на клиенте?
|
Короткий ответ Фильтрация одним проходом обычно стоит O(n). Группировка через Map тоже обычно O(n), если lookup по ключу амортизированно константный. Сортировка чаще стоит O(n log n), но реальная стоимость зависит от comparator и размера элементов. Полный ответ Фильтрация одним проходом обычно стоит const usersByRole = users.reduce((groups, user) => {
const roleUsers = groups.get(user.role) ?? [];
roleUsers.push(user);
groups.set(user.role, roleUsers);
return groups;
}, new Map<string, User[]>());Для Angular важно не только вычисление, но и rendering. Даже быстрый алгоритм может привести к лагам, если после него отрисовать тысячи DOM-узлов без virtual scroll, pagination или server-side filtering. |
Где во frontend встречаются stack, queue и priority queue?
|
Короткий ответ Stack работает по LIFO. Во frontend он встречается как call stack, undo/redo history, breadcrumbs навигации назад, обход дерева DFS без рекурсии. Полный ответ Stack работает по LIFO. Во frontend он встречается как call stack, undo/redo history, breadcrumbs навигации назад, обход дерева DFS без рекурсии. Queue работает по FIFO. Примеры: очередь toast-уведомлений, upload tasks, последовательные save operations через
Priority queue отдает элемент с наибольшим или наименьшим приоритетом. В UI она нужна редко, но может пригодиться для планирования фоновых задач, обработки событий по важности или алгоритмов графов. В обычном бизнес-коде часто лучше начать с читаемого массива и сортировки, если объем маленький и профиль не показывает проблему. |
Что такое binary search и где он может пригодиться во frontend?
|
Короткий ответ Binary search ищет значение в отсортированном массиве, каждый шаг отбрасывая половину диапазона. Сложность поиска — O(log n), но данные должны быть отсортированы и доступны по индексу. Полный ответ Binary search ищет значение в отсортированном массиве, каждый шаг отбрасывая половину диапазона. Сложность поиска —
Во frontend бинарный поиск редко нужен для обычных UI-списков. Он полезнее в больших данных: поиск позиции в timeline, виртуальный скролл с переменной высотой строк, графики, диапазоны дат, nearest point на canvas. function findFirstGreaterOrEqual(values: readonly number[], target: number): number {
let left = 0;
let right = values.length;
while (left < right) {
const middle = Math.floor((left + right) / 2);
if (values[middle] < target) {
left = middle + 1;
} else {
right = middle;
}
}
return left;
} |
Чем quicksort, mergesort и heapsort отличаются на уровне идеи?
|
Короткий ответ Quicksort выбирает pivot и делит данные на элементы меньше и больше pivot. В среднем он быстрый, но плохой выбор pivot может привести к O(n²). Полный ответ Quicksort выбирает pivot и делит данные на элементы меньше и больше pivot. В среднем он быстрый, но плохой выбор pivot
может привести к Mergesort делит массив пополам, сортирует части и сливает их. Он стабильно дает Heapsort строит heap и последовательно извлекает минимум или максимум. Он работает за В прикладном JS/TS-коде обычно используют встроенный |
Что такое heap и как он связан с priority queue?
|
Короткий ответ Heap — деревообразная структура, где родитель имеет приоритет не ниже или не выше детей. Binary heap обычно хранится в массиве: для индекса i дети находятся около 2 i + 1 и 2 i + 2. Полный ответ Heap — деревообразная структура, где родитель имеет приоритет не ниже или не выше детей. Binary heap обычно хранится в
массиве: для индекса Priority queue часто реализуют через heap: вставка и извлечение приоритетного элемента стоят Во frontend это может пригодиться для планировщика фоновых задач, графовых алгоритмов или обработки большого потока событий по приоритетам. Для обычного UI-состояния heap обычно избыточен. |
Что такое memoization и чем она отличается от caching?
|
Короткий ответ Memoization — частный случай кеширования результата чистой функции по ее аргументам. Если вход тот же, можно вернуть сохраненный результат без повторного вычисления. Полный ответ Memoization — частный случай кеширования результата чистой функции по ее аргументам. Если вход тот же, можно вернуть сохраненный результат без повторного вычисления. Caching шире: можно кешировать HTTP-ответы, изображения, compiled templates, computed state или expensive selectors. У кеша появляются вопросы invalidation, TTL, размера и прав доступа. Во frontend memoization встречается в Angular |
Что такое dynamic programming на базовом уровне?
|
Короткий ответ Dynamic programming разбивает задачу на пересекающиеся подзадачи и переиспользует их результаты. Обычно это memoization сверху вниз или табличное вычисление снизу вверх. Полный ответ Dynamic programming разбивает задачу на пересекающиеся подзадачи и переиспользует их результаты. Обычно это memoization сверху вниз или табличное вычисление снизу вверх. Для Angular-разработчика важнее не заучивать академические задачи, а узнавать паттерн: если одно и то же производное значение пересчитывается много раз, можно сохранить результат по ключу. const priceBySku = new Map<string, number>();
function getPrice(sku: string): number {
const cachedPrice = priceBySku.get(sku);
if (cachedPrice !== undefined) {
return cachedPrice;
}
const price = calculatePrice(sku);
priceBySku.set(sku, price);
return price;
} |
Что такое CPU cache и locality of reference?
|
Короткий ответ CPU cache — быстрая память рядом с процессором. Она отличается от browser cache и HTTP cache: CPU cache ускоряет доступ к данным в памяти во время вычислений, а browser/HTTP cache уменьшает сетевые загрузки и чтение ресурсов. Полный ответ CPU cache — быстрая память рядом с процессором. Она отличается от browser cache и HTTP cache: CPU cache ускоряет доступ к данным в памяти во время вычислений, а browser/HTTP cache уменьшает сетевые загрузки и чтение ресурсов. Locality of reference означает, что программа часто обращается к близким участкам памяти или повторно использует недавно прочитанные данные. Последовательный обход массива обычно дружелюбнее к CPU cache, чем хаотичные переходы по ссылкам. Во frontend это редко оптимизируют руками. Но понимание помогает объяснить, почему большие CPU-bound вычисления лучше измерять, упрощать алгоритмически, переносить в Web Worker или выполнять на сервере. |
Как спроектировать debounce и throttle как reusable utility?
|
Короткий ответ Debounce откладывает вызов до паузы в событиях, throttle ограничивает частоту вызовов. Для reusable utility важно заранее определить API: leading/trailing вызовы, cancel, flush, сохранение this, аргументы последнего вызова и тип возвращаемого значения. Полный ответ Debounce откладывает вызов до паузы в событиях, throttle ограничивает частоту вызовов. Для reusable utility важно
заранее определить API: leading/trailing вызовы, На frontend-интервью полезно привести примеры:
Частая ошибка - использовать одну технику для всех сценариев. Для network search debounce обычно лучше, а для scroll
position чаще нужен throttle или |
Реализуйте LRU cache.
|
Короткий ответ Что проверяет: Map, порядок доступа, сложность O(1). Полный ответ Что проверяет: В JavaScript class LruCache<TKey, TValue> {
private readonly values = new Map<TKey, TValue>();
constructor(private readonly capacity: number) {}
get(key: TKey): TValue | undefined {
if (!this.values.has(key)) {
return undefined;
}
const value = this.values.get(key);
if (value === undefined) {
return undefined;
}
this.values.delete(key);
this.values.set(key, value);
return value;
}
set(key: TKey, value: TValue): void {
if (this.values.has(key)) {
this.values.delete(key);
}
this.values.set(key, value);
if (this.values.size > this.capacity) {
const oldestKey = this.values.keys().next().value;
if (oldestKey !== undefined) {
this.values.delete(oldestKey);
}
}
}
}На интервью стоит обсудить, почему |
Что такое пользовательский тип данных
|
Короткий ответ Пользовательский тип описывает доменную модель приложения с помощью type, interface, класса, enum или их комбинации. Полный ответ Пользовательский тип описывает доменную модель приложения с помощью type UserId = string;
interface User {
readonly id: UserId;
readonly name: string;
readonly role: 'admin' | 'user';
}Хороший тип выражает ограничения предметной области и делает недопустимые состояния трудными для представления. Для вариантов состояния удобно использовать discriminated union, а для runtime-поведения и DI — классы. |
Что такое Union Type (тип объединения) и для чего используется?
|
Короткий ответ Union type означает, что значение может принадлежать одному из нескольких типов: Полный ответ Union type означает, что значение может принадлежать одному из нескольких типов: type RequestState<T> =
| {status: 'idle'}
| {status: 'loading'}
| {status: 'success'; data: T}
| {status: 'error'; error: string};Перед использованием специфичных свойств union нужно сузить тип через Discriminated union часто лучше набора независимых boolean-флагов: он не позволяет одновременно представить
несовместимые состояния, например |
Что такое декоратор и какие виды декораторов вы знаете?
|
Короткий ответ Декоратор — способ добавления метаданных к объявлению класса. Это специальный вид объявления, который может быть присоединен к объявлению класса, методу, методу доступа, свойству или параметру. Полный ответ Декоратор — способ добавления метаданных к объявлению класса. Это специальный вид объявления, который может быть присоединен к объявлению класса, методу, методу доступа, свойству или параметру. Декораторы используют форму @expression, где expression - функция, которая будет вызываться во время выполнения с информацией о декорированном объявлении. И, чтобы написать собственный декоратор, нам нужно сделать его factory и определить тип:
Декоратор класса Вызывается перед объявлением класса, применяется к конструктору класса и может использоваться для наблюдения, изменения или замены определения класса. Expression декоратора класса будет вызываться как функция во время выполнения, при этом конструктор декорированного класса является единственным аргументом. Если класс декоратора возвращает значение, он заменит объявление класса вернувшимся значением. export function logClass(target: Function) {
// Сохранение ссылки на оригинальный конструктор
const original = target;
// Функция генерирует экземпляры класса
function construct(constructor, args) {
const c: any = function () {
return constructor.apply(this, args);
};
c.prototype = constructor.prototype;
return new c();
}
// Определение поведения нового конструктора
const f: any = function (...args) {
console.log(`New: ${original['name']} is created`);
//New: Employee создан
return construct(original, args);
};
// Копирование прототипа, чтобы оператор intanceof работал
f.prototype = original.prototype;
// Возвращает новый конструктор, переписывающий оригинальный
return f;
}
@logClass
class Employee {}
let emp = new Employee();
console.log('emp instanceof Employee');
//emp instanceof Employee
console.log(emp instanceof Employee);
//trueДекоратор свойства Объявляется непосредственно перед объявлением метода. Будет вызываться как функция во время выполнения со следующими двумя аргументами:
function logParameter(target: Object, propertyName: string) {
// Значение свойства
let _val = this[propertyName];
// Геттер свойства
const getter = () => {
console.log(`Get: ${propertyName} => ${_val}`);
return _val;
};
// Сеттер свойства
const setter = (newVal) => {
console.log(`Set: ${propertyName} => ${newVal}`);
_val = newVal;
};
// Удаление свойства
if (delete this[propertyName]) {
// Создает новое свойство с геттером и сеттером
Object.defineProperty(target, propertyName, {
get: getter,
set: setter,
enumerable: true,
configurable: true,
});
}
}
class Employee {
@logParameter
name: string;
}
const emp = new Employee();
emp.name = 'Mohan Ram';
console.log(emp.name);
// Set: name => Mohan Ram
// Get: name => Mohan Ram
// Mohan RamДекоратор метода Объявляется непосредственно перед объявлением метода. Будет вызываться как функция во время выполнения со следующими двумя аргументами:
Декоратор параметра Объявляется непосредственно перед объявлением метода. Будет вызываться как функция во время выполнения со следующими двумя аргументами:
function logParameter(target: Object, propertyName: string, index: number) {
// Генерация метаданных для соответствующего метода
// для сохранения позиции декорированных параметров
const metadataKey = `log_${propertyName}_parameters`;
if (Array.isArray(target[metadataKey])) {
target[metadataKey].push(index);
} else {
target[metadataKey] = [index];
}
}
class Employee {
greet(@logParameter message: string): void {
console.log(`hello ${message}`);
}
}
const emp = new Employee();
emp.greet('world'); |
Зачем нам нужны определения типов, где есть JavaScript c динамической типизацией?
|
Короткий ответ Динамическая типизация удобна во время выполнения, но многие ошибки можно обнаружить раньше: Полный ответ Динамическая типизация удобна во время выполнения, но многие ошибки можно обнаружить раньше:
TypeScript добавляет статический анализ, автодополнение, безопасный рефакторинг и явные контракты между частями приложения. Типы не заменяют runtime-валидацию: данные от API, пользователя и внешних систем все равно считаются недоверенными и должны проверяться. После компиляции большинство типов удаляется, а браузер выполняет обычный JavaScript. |
Поддерживает ли TypeScript перегрузку методов?
|
Короткий ответ Да. TypeScript поддерживает несколько сигнатур перегрузки и одну общую реализацию. Полный ответ Да. TypeScript поддерживает несколько сигнатур перегрузки и одну общую реализацию. function format(value: number): string;
function format(value: Date): string;
function format(value: number | Date): string {
return value instanceof Date ? value.toISOString() : value.toFixed(2);
}Сигнатура реализации не видна вызывающему коду и должна быть совместима со всеми перегрузками. В runtime существует только одна JavaScript-функция, поэтому различение вариантов выполняет сама реализация. Если union-параметр дает такой же понятный API, обычно он проще перегрузок. |
Возможна ли перегрузка конструктора в TypeScript?
|
Короткий ответ Да, с тем же ограничением: можно описать несколько сигнатур, но реализация конструктора остается одна. Полный ответ Да, с тем же ограничением: можно описать несколько сигнатур, но реализация конструктора остается одна. class Point {
readonly x: number;
readonly y: number;
constructor();
constructor(x: number, y: number);
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
}
}Нельзя написать несколько тел |
Поддерживает ли TypeScript перегрузку методов (конструкторов)?
|
Короткий ответ TypeScript поддерживает перегрузку функций, методов и конструкторов на уровне типов. Сначала объявляются доступные вызывающему коду сигнатуры, затем одна совместимая реализация. Полный ответ TypeScript поддерживает перегрузку функций, методов и конструкторов на уровне типов. Сначала объявляются доступные вызывающему коду сигнатуры, затем одна совместимая реализация. В скомпилированном JavaScript остается одна функция или один конструктор. Поэтому перегрузка не выбирает разные реализации автоматически: код должен сам сузить аргументы. Перегрузки нужны, когда разные наборы аргументов дают разные, точно связанные возвращаемые типы. Для простых случаев предпочтительнее union types, optional-параметры или объект параметров. |
Чем type отличается от interface и что такое intersection type?
|
Короткий ответ interface описывает форму объекта, поддерживает declaration merging и удобно расширяется через extends. type может описывать не только объект, но и union, tuple, primitive alias, mapped или conditional type. Полный ответ
interface Identifiable {
readonly id: string;
}
type Timestamped = {
readonly createdAt: Date;
};
type Entity = Identifiable & Timestamped;Intersection |
Как типизировать состояние, API response и конфигурацию Angular-компонента?
|
Короткий ответ Для состояний удобен discriminated union: Полный ответ Для состояний удобен discriminated union: type LoadState<T> =
| {status: 'idle'}
| {status: 'loading'}
| {status: 'success'; data: T}
| {status: 'error'; error: string};API DTO отделяют от доменной модели и преобразуют на data-access границе. Inputs типизируют максимально узко: readonly user = input.required<Pick<User, "id" | "name">>();Конфигурации проверяют через |
Как типами описать дерево в TypeScript?
|
Короткий ответ Для дерева обычно описывают узел с payload и дочерними узлами. Если структура readonly для потребителей, это стоит отразить в типе: Полный ответ Для дерева обычно описывают узел с payload и дочерними узлами. Если структура readonly для потребителей, это стоит отразить в типе: interface TreeNode<T> {
readonly value: T;
readonly children: ReadonlyArray<TreeNode<T>>;
}Такой тип подходит для меню, router-like конфигурации, дерева категорий или результата парсинга. Если у узлов бывают разные виды, лучше использовать discriminated union. type FormNode =
| {readonly kind: 'group'; readonly controls: ReadonlyArray<FormNode>}
| {readonly kind: 'field'; readonly name: string; readonly value: string};Discriminant |
Зачем использовать readonly-типы для структур данных?
|
Короткий ответ readonly и ReadonlyArray показывают, что вызывающий код не должен менять структуру напрямую. Это особенно полезно для Angular inputs, signals, store state и derived data. Полный ответ
interface TableState {
readonly rows: ReadonlyArray<Row>;
readonly selectedIds: ReadonlySet<string>;
}Readonly-тип не делает данные глубоко immutable в runtime, но улучшает контракт и снижает риск случайной мутации. Обновление состояния лучше выражать созданием новой структуры: const nextRows = state.rows.toSorted((first, second) => first.name.localeCompare(second.name)); |
Что такое generics, generic constraints и keyof?
|
Короткий ответ Generic позволяет сохранить связь между входными и выходными типами: Полный ответ Generic позволяет сохранить связь между входными и выходными типами: function getProperty<T extends object, K extends keyof T>(value: T, key: K): T[K] {
return value[key];
}
Generics нужны для reusable API, но не должны превращать простой код в сложную type-level программу. |
Что такое mapped, conditional types и infer?
|
Короткий ответ Mapped type преобразует свойства существующего типа: Полный ответ Mapped type преобразует свойства существующего типа: type ReadonlyState<T> = {
readonly [K in keyof T]: T[K];
};Conditional type выбирает тип по условию: type ApiResult<T> = T extends Error ? {error: T} : {data: T};
type AwaitedValue<T> = T extends Promise<infer Value> ? Value : T;В прикладном коде сначала используют стандартные utility types: |
Чем satisfies отличается от as?
|
Короткий ответ satisfies проверяет совместимость значения с типом, сохраняя максимально точный выведенный тип: Полный ответ
const routes = {
home: '/',
profile: '/profile',
} satisfies Record<string, `/${string}`>;
const config = value as AppConfig;Для конфигураций, route maps и provider options предпочтителен |
Почему unknown безопаснее any и как писать type guards?
|
Короткий ответ any отключает проверку типов и распространяет небезопасность по коду. unknown требует сначала доказать форму значения. Полный ответ
function isUser(value: unknown): value is User {
if (typeof value !== 'object' || value === null) {
return false;
}
return 'id' in value && 'name' in value;
}Type guard с предикатом |
Как типизировать граф или dependency graph?
|
Короткий ответ Для adjacency list удобно использовать ReadonlyMap или Record, если ключи строковые и данные приходят из JSON. Полный ответ Для adjacency list удобно использовать type ProjectName = string;
type DependencyGraph = ReadonlyMap<ProjectName, ReadonlyArray<ProjectName>>;Если нужно хранить дополнительные данные о ребре, вводят отдельный тип: interface DependencyEdge {
readonly from: ProjectName;
readonly to: ProjectName;
readonly type: 'static' | 'dynamic';
}В frontend такие типы встречаются в визуализации зависимостей, build tooling, state machines и flows навигации. Типы фиксируют форму данных, но cycle detection и валидация внешнего JSON все равно остаются runtime-логикой. |
Когда generic data structure оправдана?
|
Короткий ответ Generic-структура оправдана, когда один алгоритм действительно работает с разными типами значений и сохраняет связь между входом и выходом. Полный ответ Generic-структура оправдана, когда один алгоритм действительно работает с разными типами значений и сохраняет связь между входом и выходом. interface Queue<T> {
enqueue(value: T): void;
dequeue(): T | undefined;
readonly size: number;
}Если структура нужна только для одного доменного типа, отдельный generic может быть лишним. Например,
|
Чем generic constraints отличаются от intersection types?
|
Короткий ответ T extends Constraint ограничивает допустимые типы для generic и разрешает обращаться к полям constraint внутри функции. T & Constraint создает новый intersection type, который требует свойства обеих частей у итогового значения. Полный ответ
function byId<T extends {readonly id: string}>(items: ReadonlyArray<T>): ReadonlyMap<string, T> {
return new Map(items.map((item) => [item.id, item]));
}Constraint говорит: "принимаю любой тип, но у него должен быть |
Как типизировать тестовые double без any?
|
Короткий ответ Для stub обычно достаточно Pick или satisfies, чтобы описать только используемую часть зависимости. Полный ответ Для stub обычно достаточно interface UserApi {
loadUser(id: string): Promise<User>;
saveUser(user: User): Promise<void>;
}
const userApiStub = {
loadUser: async () => ({id: '1', name: 'Ada'}),
} satisfies Pick<UserApi, 'loadUser'>;Так тест не зависит от лишних методов и не теряет типовую проверку. Если mock framework возвращает широкие типы, лучше
изолировать unsafe interop в маленьком helper и не распространять |
Когда нужны declaration files .d.ts?
|
Короткий ответ .d.ts описывает типы для JavaScript-кода, внешнего global API, CSS modules, assets или пакета без собственных типов. Файл не содержит runtime-кода и не должен обещать то, чего нет в реализации. Полный ответ
declare module '*.module.css' {
const classes: Readonly<Record<string, string>>;
export default classes;
}В библиотеке declaration files являются частью публичного API. Их нужно проверять вместе с build и не использовать для скрытия реальных несовпадений между TypeScript и runtime. |
Почему generic type parameter не дает runtime safety?
|
Короткий ответ Generic существует только на этапе компиляции и стирается в JavaScript. Если данные приходят из API, T не проверяет форму ответа в runtime. Полный ответ Generic существует только на этапе компиляции и стирается в JavaScript. Если данные приходят из API, async function loadJson<T>(url: string): Promise<T> {
const response = await fetch(url);
return response.json() as Promise<T>;
}Такой helper удобен, но он доверяет внешним данным. Для важных контрактов нужна runtime validation: schema, hand-written guard или adapter на границе API. Хороший ответ разделяет compile-time типы и проверку данных, которые пришли извне. Follow-up вопросы:
|



