Типизированная переменная индексирования не относится к типу индексируемого объекта. - PullRequest
1 голос
/ 09 апреля 2020

У меня есть немного сложный объект, на котором набраны клавиши на самом верхнем уровне объекта, а также на самом глубоком уровне объекта, и я не могу индексировать вторые набранные ключи object.

Это мой объект и мои интерфейсы (упрощенно):

const obj = {
  keyA: {
    variants: {
      "0": false,
      "1": false
    }
  },
  keyB: {
    variants: {
      "0-0": false,
      "0-1": false
    }
  }
}

type Obj = typeof obj;
type ObjKeys = keyof Obj;
type ObjVariants<T extends ObjKeys> = Obj[T]["variants"];

, и эта функция не может правильно индексировать selectedKeys.variant с использованием defaultVariant.

function getVariant<T extends ObjKeys>(key: T, defaultVariant: keyof ObjVariants<T>) {
  const selectedKey = obj[key];
  const selectedVariant = selectedKey.variants[defaultVariant];
}

Я получаю ошибку:

Type 'keyof { keyA: { variants: { 0: boolean; 1: boolean; }; }; keyB: { variants: { "0-0": boolean; "0-1": boolean; }; }; }[T]["variants"]' cannot be used to index type '{ 0: boolean; 1: boolean; } | { "0-0": boolean; "0-1": boolean; }'.(2536)

Похоже, это связано с этими двумя проблемами: https://github.com/microsoft/TypeScript/issues/21760 и https://github.com/microsoft/TypeScript/issues/36631 Но я Я не уверен, что «обходной путь» для второго работает для меня, я чувствую, что я что-то упустил

Вот ссылка на игровую площадку: https://www.typescriptlang.org/play/index.html?ssl=1&ssc=1&pln=65&pc=3#code / MYewdgzgLgBCBGArGBeGBvAUDGBrApgJ4CCAXBtjjAG4CGATgJa4HJHGHGHGHGHGHPHGHPHGHPHBHPHBHPHBHPHPHBHPHPHBHPHRHBHPHRHBHBHRKHRKHRKHRKHRKHRKHRKHRKHRKHBHBHRKHBHBHBHRHBKHRKHRKHRIHRIHRIHRIHRIHBIHGTTTTTTTTTTTTTTTZHGHRIHRIHRIHRIHMI + Q0eO0Xr8u8tUacGjZiiEADvgwAPJIqDC + ASCCcEgA3D7 + gSGIANJEEGG6UcFxCQE5iABqDMysEAA8ACow + AAeUPhgACYZyWmEEAB8YckA2pUAur3cxqVs3APxmJiCAK5gwFCM4DAA5vhQxSasVTX1jS0F7V0AFLrklRIwTfjCsyKbJabkWdHJW2MVlZ0AlBQ4oEgsHEInwi3 wTXaYQQiF6ukmlEB0BgILBDSaH1MYVR4MhRAAdKNTBBejc7g9MawERogA

Кто-нибудь еще сталкивался с чем-то похожим / имеет предложенный обходной путь?

PD: я пытался добавить строгий тип к obj, используя *1029* и обнаружил .

1 Ответ

1 голос
/ 09 апреля 2020

Похоже на поведение по замыслу или другими словами ограничение конструкции. Такую функцию сложно защитить типом, так как мы можем также передавать подтипы. Например, T extends ObjKeys охватывает также объединение keyA | keyB, это означает, что это ограничение не может предполагать, что это будет один из вариантов.

Чтобы полностью установить ограничение, мы можем go использовать следующий подход:

function getVariant
<O extends { [K in K1]: { variants: { [KK in K2]: Obj[K1]["variants"][KK] } } }
, K1 extends keyof Obj
, K2 extends keyof Obj[K1]["variants"]>
  (o: O, key: K1, defaultVariant: K2) {
  const selectedKey = o[key].variants;
  const selectedVariant = selectedKey[defaultVariant];
}
// using
getVariant(obj, 'keyA', '0')

Как видите, я сделал дополнительный параметр, чтобы сузить тип для ключей K1 и K2. Самая важная строка - { [K in K1]: { variants: { [KK in K2]: Obj[K1]["variants"][KK] } } }. Это означает, что мы имеем дело с типом, который охватывает оба ключа, но все же определение совместимо с нашим типом obj.

Мы все еще можем использовать obj в качестве внешней константы, но тогда нам нужно использовать введите утверждение:

function getVariant<K1 extends keyof Obj, K2 extends keyof Obj[K1]["variants"]>
  (key: K1, defaultVariant: K2) {
  let _obj = obj as { [K in K1]: { variants: { [KK in K2]: Obj[K1]["variants"][KK] } } };
  const selectedKey = _obj[key].variants;
  const selectedVariant = selectedKey[defaultVariant];
}
// using
getVariant('keyA', '0')

Почему это работает? Это работает, потому что мы статически устанавливаем тип с точными типами, которые мы имеем / будем иметь. Если мы говорим T extends A, это не означает, что T - это A, это также не означает, что T - это некоторый вариант A. Говоря, T extends A мы сужаем, ограничиваем тип T для присвоения A Поэтому мы не строги. Делая тип {[K in T]: X}, T extends Y, мы строго определяем, что тип будет иметь точный ключ T, и это будет строго один тип из возможных присваиваемых типов до Y. Такое строгое определение равно установке значения c типа Obj['keyA'].

...