#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 Я сделал обновление