Как создать сопоставленный тип из ключей фиксированного объекта в Typescript - PullRequest
0 голосов
/ 28 апреля 2018

У меня есть такой объект:

const routes = {
  home: { path: '/', page: 'home' },
  profile: { path: '/profile', page: 'users/profile' }
}

Я хотел бы определить производный тип от этого, например так:

type RouteName = keyof typeof routes, который создает тип как "home" | "profile".

Однако я не могу тогда сделать:

for (let name in routes) {
  router.add({ name, ...routes[name]})
}

потому что компилятор жалуется на routes[name] неявного типа any:

Element implicitly has an 'any' type because type '{ home: { path: string; page: string; }; profile: { path: string; page: string; };' has no index signature.

Если я изменю определение маршрутов на:

interface RouteDefinition {
  path: string
  page: string
}
const routes: {[key: string]: RouteDefinition} = {
  home: { path: '/', page: 'home' },
  profile: { path: '/profile', page: 'users/profile' }
}

сгенерированный тип type RouteName = keyof typeof routes теперь string вместо "home"|"profile".

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

Объект должен быть определен только один раз и никогда не должен быть переназначен. Я пробовал кучу комбинаций Readonly<>, кастинга и т. Д., Но не могу понять. Есть ли способ сделать это?

(я использую Typescript 2.8.1)

1 Ответ

0 голосов
/ 28 апреля 2018

TypeScript не считает безопасным предполагать, что ключи for..in - это в точности ключи, определенные в типе, потому что в JavaScript все объекты открыты.

Вы можете использовать утверждение для устранения ошибки компиляции:

for (let name in routes) {
  routes[name as RouteName]; // no error
}

Или то, что я хотел бы сделать, это объединить два ваших подхода. Вы можете определить routes, как вы делаете, извлечь ключи как RouteName, но также сделать RouteDefinition и назначить свои маршруты индексированному типу (что можно сделать, используя утверждение для новой переменной, или параметр функции), когда вы хотите отобразить их:

interface RouteDefinition {
    path: string;
    page: string;
}

const routes = {
    home: { path: '/', page: 'home' },
    profile: { path: '/profile', page: 'users/profile' }
}

type RouteName = keyof typeof routes;

mapRoutes(routes);

function mapRoutes(routes: { [key: string]: RouteDefinition }) {
    for (let name in routes) {
        routes[name] // no error
    }
}

Если ваш литерал routes не удовлетворяет RouteDefinition (отсутствует ключ, ключ неправильного типа), вы получите ошибку на сайте назначения, то есть mapRoutes(routes) выше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...