Определение типов для объектов с фиксированными ключами эндпоинтов

Параметризуйте базовый контракт конкретным объединением ключей endpoints. Тогда один общий тип проверит структуру, не расширяя имена методов до произвольной строки.

Короткий ответ

Что ответить на собеседовании

Параметризуйте базовый контракт конкретным объединением ключей endpoints. Тогда один общий тип проверит структуру, не расширяя имена методов до произвольной строки.

Подробный разбор

Ответ с пояснениями

Условие

Необходимо разработать типизацию, которая корректно описывает два разных объекта, при этом сохраняет точные типы ключей внутри свойства endpoints. Стандартные подходы вроде key: string или Record<string, ...> не подходят, так как они размывают конкретные имена методов.

Предполагается, что набор ключей эндпоинтов известен на этапе компиляции для каждого объекта:

  • для vtemplateObject – getVtemplates и postVtemplates;
  • для reportObject – getReports и putReports.

Задача состоит в том, чтобы описать типы так, чтобы TypeScript «знал», какие именно свойства находятся внутри endpoints, не меняя базовый тип объектов.

//-- 1 -- Задаёт структуру объектов, не знает про конкретные объекты и их методы


//-- 2 -- Не может менять базовый тип, но знает про структуру объекта который типизирует

const vtemplateObject = {
    entity: "vtemplate",
    endpoints: {
        getVtemplates: {
            method: "GET",
            url: "vtemplate"
        },
        postVtemplates: {
            method: "POST",
            url: "vtemplate"
        }
    }
}

const reportObject = {
    entity: "report",
    endpoints: {
        getReports: {
            method: "GET",
            url: "report"
        },
        putReports: {
            method: "PUT",
            url: "report"
        }
    }
};

Требуется построить тип (или набор типов), который будет принимать такие объекты и сохранять точные типы их эндпоинтов, не прибегая к обобщённым Record<string, …> и не меняя саму структуру объектов.

Ответ

Параметризуйте базовый контракт конкретным объединением ключей endpoints. Тогда один общий тип проверит структуру, не расширяя имена методов до произвольной строки.

type Endpoint = { method: string; url: string };
type ApiObject<K extends PropertyKey> = {
  entity: string;
  endpoints: { [P in K]: Endpoint };
};
type Vtemplate = ApiObject<keyof typeof vtemplateObject.endpoints>;
type Report = ApiObject<keyof typeof reportObject.endpoints>;
const checkedVtemplate: Vtemplate = vtemplateObject;
const checkedReport: Report = reportObject;

GetVtemplates и postVtemplates сохраняются как конкретные ключи. Method здесь string, как у уже объявленных объектов; для строгого объединения HTTP-методов литералы проверяют при создании через satisfies или as const. Сами данные не преобразуются.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее