Версия TypeScript: 3.0.3
Условия поиска: тип на основе значений в массиве
Есть ли текущая или планируемая функция для создания типа из строк в массиве?
Код
const values = ['A', 'B']
type Foo = OneOf<values> // Is there a way of doing this?
const v1: Foo = 'A' // This should work
const v2: Foo = 'D' // This should give me an error since 'D' doesn't exist in values
Аналогично тому, как работает keyof
:
const values = {
A: 'A',
B: 'B'
}
type Foo = keyof typeof values
const v1: Foo = 'A'
const v2: Foo = 'D' // Type '"D"' is not assignable to type '"A" | "B"'
Связанные вопросы:https://github.com/Microsoft/TypeScript/issues/20965
Ссылка на игровую площадку http://www.typescriptlang.org/play/#src = let% 20vals1% 20% 3D% 20% 5B'A '% 2C% 20'B'% 5D% 0D% 0Atype% 20Foo1% 20% 3D% 20OneOf% 3Cvals1% 3E% 20% 2F% 2F% 20Is% 20there% 20a% 20way% 20of% 20doing% 20this% 3F% 0D% 0A% 0D% 0Alet% 20v1% 3A% 20Foo1% 20% 3D% 20 ' A '% 20% 2F% 2F% 20Это% 20 должно% 20work% 0D% 0Alet% 20v2% 3A% 20Foo1% 20% 3D% 20'D'% 20% 2F% 2F% 20Это% 20 должно% 20give% 20me% 20an% 20 error% 20, поскольку% 20'D '% 20 не существует% 20exist% 20in% 20values% 0D% 0A% 0D% 0Alet% 20values2% 20% 3D% 20% 7B% 0D% 0A% 20% 20A% 3A% 20'A '% 2C% 0D% 0A% 20% 20B% 3A% 20'B'% 0D% 0A% 7D% 0D% 0Atype% 20Foo2% 20% 3D% 20keyof% 20typeof% 20vals2% 0D% 0Alet% 20v3% 3A% 20Foo2 % 20% 3D% 20'A '% 0D% 0Alet% 20v4% 3A% 20Foo2% 20% 3D% 20'D'% 20% 2F% 2F% 20Type% 20 '% 22D% 22'% 20is% 20not% 20 назначается % 20to% 20тип% 20 '% 22A% 22% 20% 7C% 20% 22B% 22'
keyof
работает только потому, что использует информацию, известную статически во время компиляции. Типы в TS полностью стираются и не существуют в транслируемом коде, поэтому просто невозможно создать тип, основанный на содержимом массива во время выполнения.
Это возможно:
function stringLiterals<T extends string>(...args: T[]): T[] { return args; }
type ElementType<T extends ReadonlyArray<unknown>> = T extends ReadonlyArray<infer ElementType> ? ElementType : never;
const values = stringLiterals('A', 'B');
type Foo = ElementType<typeof values>;
const v1: Foo = 'A' // This should work
const v2: Foo = 'D' // This should give me an error since 'D' doesn't exist in values
Хотя точно не понятно, как это сделать. # 27179 имеет отношение.
Тем не менее, он по-прежнему использует информацию, статически известную компилятору; в этот момент я не понимаю, в чем преимущество простого слова ”A” | “B”
.
@fatcerberus Полезно избегать повторения информации - если вы просто напишете const values = ["A", "B"]; type Foo = "A" | "B";
, кто-то может легко изменить одну из них, забыв изменить другую. Вы можете написать const values: Foo[] =["A", "B"];
, но это все равно может добавить дополнительную запись к Foo
и забыть поместить ее в values
.
Где мы сейчас находимся?
Как сказал @andy-ms, было бы действительно полезно избегать повторения информации вручную.
И, как сказал @fatcerberus , типы полностью стираются. Иногда нам нужно проверять данные во время выполнения, которые мы получаем, например, из не чистой части приложения (например, веб-службы или от пользователя).
Основываясь на ответе @andy-ms и используя утверждения const, представленные в typescript 3.4, теперь можно сделать что-то вроде этого
const values = ['A', 'B'] as const
type ElementType < T extends ReadonlyArray < unknown > > = T extends ReadonlyArray<
infer ElementType
>
? ElementType
: never
type Foo = ElementType<typeof values> // this is correctly inferred as literal "A" | "B"
Эта проблема была помечена как "Вопрос" и в последнее время не наблюдалась. Он был автоматически закрыт на хозяйственные нужды. Если вы все еще ждете ответа, вопросы обычно лучше подходят для stackoverflow .
в [email protected] и выше. реши это так
export const type = <const>[
'room',
'room_with_gifter',
'user_send'
];
export interface Activity {
id?: string;
type: typeof type[number];
}
Вместо этого:
export const items = <const>[
'room',
'room_with_gifter',
'user_send'
];
export interface Activity {
id?: string;
type: typeof items[number];
}
Ты можешь это сделать:
export const items = [
'room',
'room_with_gifter',
'user_send'
] as const;
export type Item = typeof items;
export interface Activity {
id?: string;
type: Item;
}
_ Очень не нравится использовать type
в качестве имени переменной. _
@ 7 км
в [email protected] и выше. реши это так
export const type = <const>[ 'room', 'room_with_gifter', 'user_send' ]; export interface Activity { id?: string; type: typeof type[number]; }
Что, если это будет у меня в классе?
class Class {
const list = <const>[
'room',
'room_with_gifter',
'user_send'
];
const withType: typeof list[number];
}
Не работает. Я тоже не умею делать this.list[number]
. Кроме того, для моей конкретной
usecase, я не могу установить list
равным static
@ 7 км
в [email protected] и выше. реши это так
export const type = <const>[ 'room', 'room_with_gifter', 'user_send' ]; export interface Activity { id?: string; type: typeof type[number]; }
Что, если это будет у меня в классе?
class Class { const list = <const>[ 'room', 'room_with_gifter', 'user_send' ]; const withType: typeof list[number]; }
Не работает. Я тоже не умею делать
this.list[number]
. Кроме того, для моей конкретной
usecase, я не могу установитьlist
равнымstatic
@vegerot Вы не можете использовать const
непосредственно в области действия class
, поэтому синтаксис неверен.
@ 7 км
в [email protected] и выше. реши это так
export const type = <const>[ 'room', 'room_with_gifter', 'user_send' ]; export interface Activity { id?: string; type: typeof type[number]; }
Что, если это будет у меня в классе?
class Class { const list = <const>[ 'room', 'room_with_gifter', 'user_send' ]; const withType: typeof list[number]; }
Не работает. Я тоже не умею делать
this.list[number]
. Кроме того, для моей конкретной
usecase, я не могу установитьlist
равнымstatic
@vegerot Вы не можете использовать
const
непосредственно в области действияclass
, поэтому синтаксис неверен.
@ 7kms Моя плохая. Я слишком много копировал и вставлял. Я хотел сказать
class Class {
private list = <const>[
'room',
'room_with_gifter',
'user_send'
];
private withType: typeof list[number];
// OR
private withType2: typeof this.list[number];
}
и т.д. все не работает.
Кроме того, для моего конкретного случая использования я не могу сделать список статическим
@ 7 км
в [email protected] и выше. реши это так
export const type = <const>[ 'room', 'room_with_gifter', 'user_send' ]; export interface Activity { id?: string; type: typeof type[number]; }
Что, если это будет у меня в классе?
class Class { const list = <const>[ 'room', 'room_with_gifter', 'user_send' ]; const withType: typeof list[number]; }
Не работает. Я тоже не умею делать
this.list[number]
. Кроме того, для моей конкретной
usecase, я не могу установитьlist
равнымstatic
@vegerot Вы не можете использовать
const
непосредственно в области действияclass
, поэтому синтаксис неверен.@ 7kms Моя плохая. Я слишком много копировал и вставлял. Я хотел сказать
class Class { private list = <const>[ 'room', 'room_with_gifter', 'user_send' ]; private withType: typeof list[number]; // OR private withType2: typeof this.list[number]; }
и т.д. все не работает.
Кроме того, для моего конкретного случая использования я не могу сделать список статическим
class Class {
private list = [
"room",
"room_with_gifter",
"user_send"
] as const
private withType: Class["list"][number]
}
@vegerot Как насчет этого?
@eyalch это typeof
или что-то в этом роде. Не могли бы вы быстро объяснить, как это работает, или свяжите меня с руководством, где это обсуждается?
@eyalch это
typeof
или что-то в этом роде. Не могли бы вы быстро объяснить, как это работает, или свяжите меня с руководством, где это обсуждается?
@vegerot Конечно, я считаю, что это "индексный тип". Вот он в Руководстве: https://www.typescriptlang.org/docs/handbook/advanced-types.html#index -types
Рад, что смог помочь!
Привет,% username%, если решение ElementType
не работает и для вас,
обратите внимание на as const
после массива . Это важно
Самый полезный комментарий
в [email protected] и выше. реши это так