interface baseVal {
val: string
}
interface parentA{
checker: baseVal
}
interface parentB{
someone: baseVal
}
let test = <T extends parentA | parentB>(param: T) => {
const keys = Object.keys(param) as (keyof T)[]
keys.forEach(key => {
let data = param[key] as baseVal // error
})
}
В настоящее время я получаю сообщение об ошибке «let data = param [key] as baseVal». В сообщении об ошибке указано следующее:
Преобразование типа «T [keyof T]» в тип «baseVal» может быть ошибкой, потому что ни один из типов в достаточной степени не перекрывается с другим. Если это было намеренно, сначала преобразуйте выражение в «неизвестно».
Почему машинописный текст не понимает, что значения в интерфейсах одного типа? Как я могу создать интерфейсы, чтобы машинописный текст знал об этой связи?
2 ответа
Собственно, что происходит, когда у вас есть тип parentA | parentB - это тип вообще без ключей, потому что union (pipe) ведет себя не так, как вы ожидаете в случае интерфейсов. См. https://www.typescriptlang.org/docs/handbook. /advanced-types.html#union-types:
Если значение имеет тип A | B, мы знаем только наверняка, что у него есть члены, которые есть как у A, так и у B. В этом примере у Bird есть член с именем fly. Мы не можем быть уверены, что переменная типа Bird | У рыбы есть нахлыстовой метод. Если во время выполнения переменная действительно является Fish, то вызов pet.fly () завершится ошибкой.
Вы можете убедиться в этом сами, введя строку let x = param.someone.val;
. Вы увидите, как intellisense жалуется, что param
не имеет члена someone
. Это потому, что param
может иметь тип parentA, и в этом случае эта строка вызовет исключение во время выполнения, потому что param.someone
не определено. Короче говоря, здесь нельзя использовать тип объединения.
Одна из основных целей машинописного текста - предотвращение ошибок времени выполнения из-за неправильного набора текста, поэтому, если объект, переданный функции, может быть одного из двух типов, вы не можете получить доступ к полю, принадлежащему только одному из этих типов. В некоторых случаях вы можете использовать проверку типов, как описано на связанной странице в этой ситуации, но в вашем случае проблема не так легко решается. Обратите внимание, что вы перебираете ключи param
. Представьте, что у типов было более одного ключа. Как машинописный текст определит тип параметра [ключ]? В предоставленном коде человек может увидеть, что это за тип, но в целом такой подход недопустим. Поэтому, если вы хотите сохранить код примерно в том виде, в каком он у вас есть, вам может потребоваться что-то вроде:
interface baseVal {
val: string
}
interface parentA{
checker: baseVal;
}
interface parentB{
someone: baseVal;
}
interface parent {
[key: string]: baseVal;
}
let test = <T extends parent>(param: T) => {
const keys = Object.keys(param) as (keyof T)[]
keys.forEach(key => {
let data = param[key] as baseVal;
})
}
(Обратите внимание, что это не подходит, если у ваших фактических типов больше ключей с разными типами, но тогда ваш код все равно будет недействительным.)
Вы можете выполнить утверждение типа дважды, так как в сообщении об ошибке TS указано таким образом:
let test = <T extends parentA | parentB>(param: T) => {
const keys = Object.keys(param) as (keyof T)[]
keys.forEach(key => {
let data = param[key] as unknown as baseVal
})
}
Однако учтите, что с утверждением типа вы обойдете некоторые проверки TS в этой части.
Похожие вопросы
Новые вопросы
javascript
По вопросам программирования на ECMAScript (JavaScript / JS) и его различных диалектах / реализациях (кроме ActionScript). Включите все соответствующие теги в свой вопрос; например, [node.js], [jquery], [json] и т. д.