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» может быть ошибкой, потому что ни один из типов в достаточной степени не перекрывается с другим. Если это было намеренно, сначала преобразуйте выражение в «неизвестно».

Почему машинописный текст не понимает, что значения в интерфейсах одного типа? Как я могу создать интерфейсы, чтобы машинописный текст знал об этой связи?

0
Xavier 22 Апр 2020 в 02:58

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; 
    })
} 

(Обратите внимание, что это не подходит, если у ваших фактических типов больше ключей с разными типами, но тогда ваш код все равно будет недействительным.)

1
see sharper 22 Апр 2020 в 02:00

Вы можете выполнить утверждение типа дважды, так как в сообщении об ошибке 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 в этой части.

-1
Oscar Velandia 22 Апр 2020 в 00:29