#typescript
#typescript
Вопрос:
Предполагается, что у меня есть такой тип:
type TInfoGeneric<TType extends string, TValue> = {
valueType: TType,
value: TValue, // Correspond to valueType
}
Чтобы не повторяться, я создаю карту типов, в которой перечислены возможные, valueType
и совпадающие valueType
, ну, с типом значения.
type TInfoTypeMap = {
num: number;
str: string;
}
Теперь, чтобы действительно создать TInfo
, я использую mapped type для отображения всех типов в TInfoGeneric
, а затем получаю только его значение.
type TAllPossibleTInfoMap = {
[P in keyof TInfoTypeMap]: TInfoGeneric<P, TInfoTypeMap[P]>;
};
type TInfo = TAllPossibleTInfoMap[keyof TAllPossibleTInfoMap]; // TInfoGeneric<"num", number> | TInfoGeneric<"str", string>
Затем, чтобы определить обработчики для всех типов, я создаю другой сопоставленный тип только для обработчиков.
type TInfoHandler = {
[P in keyof TInfoTypeMap]: (value: TInfoTypeMap[P]) => any
};
const handlers: TInfoHandler = {
num: (value) => console.log(value.toString(16)),
str: (value) => console.log(value),
}
И, наконец, чтобы действительно использовать обработчик, я создаю функцию, подобную этой:
function handleInfo(info: TInfo) {
handlers[info.valueType](info.value); // Error
}
Я получил эту ошибку:
Argument of type 'string | number' is not assignable to parameter of type 'number amp; string'.
Type 'string' is not assignable to type 'number amp; string'.
Type 'string' is not assignable to type 'number'.
Обычно понятно, что handlers[info.valueType]
может быть ((value: number) => any) | ((value: string) => any)
. Однако в этом случае:
- Если
info.valueType
есть'num'
, то мы можем быть уверены, чтоhandlers[info.valueType]
есть(value: number) => any)
иinfo.value
естьnumber
. Таким образом,handlers[info.valueType]
может вызываться с помощьюinfo.value
. - Если
info.valueType
есть'str'
, то мы можем быть уверены, чтоhandlers[info.valueType]
есть(value: string) => any)
иinfo.value
естьstring
. Таким образом,handlers[info.valueType]
может вызываться с помощьюinfo.value
.
Я не уверен, является ли это ограничением Typescript или нет, но возможно ли написать код в этом стиле, чтобы он проверялся на тип?
Комментарии:
1. Ах, еще один вопрос, связанный с типами записей . Мне действительно нужно открыть новую проблему по этому поводу. Короткий ответ будет заключаться в том, что вы должны использовать утверждение типа и забыть об этом.
2. Хорошо, я только что открыл эту проблему .
Ответ №1:
Да, здесь для вас нет удобного и типобезопасного решения. Я открыл проблему в Microsoft / TypeScript # 30581 по этому поводу, но я не ожидаю, что она будет решена.
Я вижу два основных пути продвижения вперед. Один из них — просто использовать утверждение типа, поскольку вы на законных основаниях знаете здесь больше, чем компилятор. Это может быть так:
function handleInfo(info: TInfo) {
// assert your way out. Not type safe but convenient!
(handlers[info.valueType] as (x: number | string)=>any)(info.value);
}
Теперь ошибки нет. Это небезопасно для ввода. Но это удобно и не изменяет отправленный JavaScript.
Или вы могли бы попытаться провести компилятор по случаям и доказать ему, что все в порядке. Это сложно, хрупко и имеет последствия во время выполнения:
const typeGuards: {
[P in keyof TInfoTypeMap]: (x: TInfoTypeMap[keyof TInfoTypeMap])=>x is TInfoTypeMap[P];
} = {
num: (x:any): x is number => typeof x === "number",
str: (x:any): x is string => typeof x === "string"
}
function narrowTInfo<K extends keyof TAllPossibleTInfoMap>(
x: TInfo, v: K): x is TAllPossibleTInfoMap[K] {
return typeGuards[v](x.value);
}
function handleInfo(info: TInfo) {
if (narrowTInfo(info, "num")) {
handlers[info.valueType](info.value); // okay
} else {
handlers[info.valueType](info.value); // okay
}
}
Это работает, но неприятно. Поэтому я бы рекомендовал утверждение.
Надеюсь, это поможет; удачи!
Комментарии:
1. Спасибо за ваш ответ! Я тоже собирался сообщить о проблеме, но я действительно не могу объяснить проблему так хорошо, как вы (я не являюсь носителем английского языка). Мне придется использовать метод утверждения, поскольку фактический тип в моей программе — это не просто число и строка, а интерфейсы.