Можете ли вы получить точный текущий индексированный ключ в объявлении типов?

Я пытаюсь создать относительно простой тип, который будет обеспечивать ссылку на ключ вложенного объекта на внешний уровень.

Чтобы немного прояснить ситуацию, вот пример:

const obj = {
  foo: {
     name: 'bar',
     ref: 'foo' // < this should reference the key of the parent
  }
}

Это, очевидно, не будет проблемой в статическом типе объекта, где нам нужно только проверить 'foo', но я пытаюсь убедиться, что он динамический с аргументами универсального типа.

Я пробовал следующее:

type InnerObject<K> = {
   name: string,
   ref: K,
}

type OuterObject<T, K extends keyof T> = {
   [key in K]: InnerObject<K>
}

Это работает почти нормально, обеспечивая проверку типов для объединения текущих ключей:

введите здесь описание изображения

Проблема в том, что он также позволяет передавать другой ключ объекта в свойство ref, когда это явно не следует.

введите здесь описание изображения

Я думал о том, чтобы каким-то образом создать универсальный тип третьего типа и передать его в Exclude служебный тип, т.е.

type InnerObject<T, K extends keyof T, CurrentKey> = {
   name: string,
   ref: Exclude<CurrentKey, K>
}

но проблема в том, что я не могу придумать способ передачи текущего ключа индексированного объекта без написания явной функции редуктора/карты для внешнего объекта.

Возможно ли это, или это просто невозможно в typescript ?


Here's link to the playground in question.


person Samuel Hulla    schedule 31.10.2020    source источник


Ответы (1)


Если у вас есть такой тип, как Example, который вы хотите преобразовать в эту структуру, вы можете сделать это следующим образом:

type OuterObject<T extends object> = {
    [K in keyof T]: InnerObject<K>
}

Идея состоит в том, что мы сопоставляем InnerObject<K> по < em>каждый K в keyof T.

Важно отметить, что разница между этим и тем, что вы написали, заключается в том, что вы работали со всем объединением keyof T, а не с каждым элементом в объединении. Это разница между {[X in Y]: F<X>}, который оценивает, возможно, разные типы для каждого свойства, и {[X in Y]: F<Y>}, который этого не делает.

Это производит поведение, которое вы ищете:

const obj: OuterObject<Example> = {
    foo: {
        name: 'whatever',
        ref: 'foo',
    },
    bar: {
        name: 'baz',
        ref: 'foo' // error!
    }
}

Это работает для вас?

Playground link to code

person jcalz    schedule 31.10.2020
comment
Ооо это умно. Я не знал, что вы можете создать сопоставленный общий код внутри фактического объявления ключа. Я был довольно близок к этому, в основном просто сделал ошибку, объявив его общим, что привело к объединению двух ключей, а не точного сопоставленного ключа, как вы правильно указали в своем редактировании. Благодарю вас! :-) - person Samuel Hulla; 01.11.2020