Параметризуйте базовый контракт конкретным объединением ключей 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. Сами данные не преобразуются.