Сопоставление между типом и строкой и наоборот в машинописном тексте

#typescript

Вопрос:

По какой-то причине мне немного трудно решить эту проблему. У меня есть некоторые типы , такие как BlogPost или Page , и они должны сопоставляться со своими собственными строковыми идентификаторами 'blog-post' или 'page' . Очевидное решение состоит в том, чтобы включить type поле:

 interface Document {
    type: string,
}


type BlogPost = {
    type: 'blog-post',
    title: string,
}

type Page = {
    type: 'page',
    // ...
}
 

Хорошо, но теперь у нас есть какой-то общий интерфейс, и нам нужен доступ к этому типу! Итак, может быть, мы сможем определить тип утилиты, такой как:

 type DocumentType<T extends Document> = T['type'];
 

Отлично, сейчас DocumentType<BlogPost> = 'blog-post' … но это 'blog-post' в определенном смысле. Я не могу просто так поступить, как:

 const type: string = DocumentType<BlogPost>;
 

Так как же нам… извлечь этот тип? Как мы можем получить эти данные, не делая что-то нелепо ручное, например:

 function GetType<T extends BlogPost>(): string {
    return 'blog-post';
}
 

потому что это раздражает по очевидным причинам.

В идеале было бы лучше вообще пропустить это type поле! И сделайте вместо этого что-нибудь вроде этого:

 type DocumentTypes = {
    'blog-post': BlogPost,
    'page': Page,
}
 

И это круто, потому что тогда мы сможем повернуть вспять!

 type DocumentFromType<S extends keyof DocumentTypes> = DocumentTypes[S];

// DocumentFromType<'blog-post'> === BlogPost
 

Но… Я, кажется, не могу понять, как поступить по-другому!

Есть какие-нибудь мысли? Спасибо,

Редактировать вот пример использования:

Причина в том, что API, который я использую, требует строковых типов, т. е. запроса «document.type» = = «сообщение в блоге». Я хотел бы, чтобы Query<BlogPost>().all() , следовательно, мы должны преобразовать тип BlogPost в строку blog-post.

Комментарии:

1. Можете ли вы предоставить немного больше подробностей о фактическом случае использования, потому что я изо всех сил пытаюсь его найти? Вы не можете создать «сопоставление» между типом и значением. Должен существовать реальный объект или функция, которая сопоставляет одно значение с другим.

2. Причина в том, что API, который я использую, требует строковых типов, т. е. запроса «document.type» = = «сообщение в блоге». Я хотел бы, чтобы Query<BlogPost>().all() , следовательно, мы должны преобразовать тип BlogPost в строку blog-post.

3. Я предполагаю, что вы пришли из C# или аналогичного статически типизированного языкового фона. В этих языках тип является фактическим объектом (пример C# docs.microsoft.com/en-us/dotnet/api/system.type?view=net-5.0 ). Это означает, что универсальный параметр является фактическим «параметром» функции, которую вы можете использовать. Пример: public static string Query<T>() => typeof(T).FullName; В Typescript это не так — вы не сможете получить объект из типа.

4.Лучше всего сделать что-то вроде этого query<BlogPost>("blog-post") , Хорошая вещь в этом заключается в том, что intellisense будет активирован после ввода, query<BlogPost>( поэтому вам не нужно будет заполнять значение вручную.

5. В вашем случае также, возможно, стоит рассмотреть пример перечисления

Ответ №1:

Вы можете добиться чего-то подобного, но тогда вам нужно определить все разрешенные типы сущностей в одном месте, например, кортеж.

 interface Document {
    type: string,
}

type Base = { type: string };

type BlogPost = Base amp; {
    type: 'blog-post',
    title: string,
}

type Page = Base amp; {
    type: 'page',
    // ...
}

type MyTypes = [
    BlogPost,
    Page
]

type Iterate<T extends Base[], Result extends Record<string, Base> = {}> =
    (T extends []
        ? Result
        : (T extends [infer Head, ...infer Tail]
            ? (Tail extends Base[]
                ? (Head extends Base
                    ? Iterate<Tail, Result amp; Record<Head['type'], Head>>
                    : never)
                : never)
            : never)
    )

type DocumentTypes = Iterate<MyTypes>
 

Игровая площадка

Обновить

Немного читабельный способ:

 interface Document {
    type: string,
}

type Base = { type: string };

type BlogPost = Base amp; {
    type: 'blog-post',
    title: string,
}

type Page = Base amp; {
    type: 'page',
    // ...
}

type MyTypes = [
    BlogPost,
    Page
]



type Rename<T extends Base[], Prop> =
    (Prop extends keyof T
        ? (T[Prop] extends { type: infer Type }
            ? Type amp; string
            : never)
        : never)

type Iterate<T extends Base[]> =
    {
        [Prop in keyof T as Rename<T, Prop>]: T[Extract<Prop, string>]
    }

type DocumentTypes = Iterate<MyTypes>
 

Игровая площадка

Комментарии:

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

2. @ThorCorreia Я сделал обновление