Почему Typescript приводит мой тип ключа к типу «никогда» и как мне это исправить? - PullRequest
1 голос
/ 02 апреля 2020

Извините, если это дурак, я новичок в TypeScript, и мне сложно выяснить, связаны ли похожие вопросы, потому что многие из них делают очень сложные вещи. В любом случае, проблема в том, что у меня есть относительно простая установка, которую душит TS, потому что она приводит тип к never, и я не очень понимаю, почему он это делает. Вот настройка:

interface BigObject {
  foo: {
    a?: string
    b?: string
  }
  bar: {
    c?: string
    d?: string
  }
}

const instance: BigObject = {
  foo: {
    a: "a",
    b: "b",
  },
  bar: {
    c: "c",
    d: "d",
  }
}

function metafunction(bigObjProp: keyof BigObject) {
  type LittleObject = BigObject[typeof bigObjProp]
  // IDE hints show this ^^ as being correct, i.e. either of the two "sub interfaces"

  return function (littleObjProp: keyof LittleObject) { // <== littleObjProp is resolving to never
    return function (bigObject: BigObject) {
      const littleObject = bigObject[bigObjProp]
      return littleObject ? littleObject[littleObjProp] : "fallback value"
    }
  }
}

const firstClosure = metafunction("foo")
const secondClosure = firstClosure("a") // <== Argument of type "a" is not assignable to type "never"
const value = secondClosure(instance)

Я ожидаю, что значение value будет "a".

Я не понимаю, почему littleObjProp разрешается в never. Я предполагаю, что, поскольку LittleObject строится из типа аргумента, переданного в metafunction, TS будет выбирать, какой «подинтерфейс» использовать для любого данного вызова. Так, например, когда я вызываю metafunction("foo"), TS устанавливает LittleObject в { a?: string; b?: string }, и, таким образом, когда я вызываю firstClosure("a"), он говорит: «ах, да,« a »действительно является действительным ключом LittleObject , продолжать". Однако он не может этого сделать, потому что всегда думает, что keyof LittleObject означает never.

Может ли кто-нибудь помочь мне понять 1) почему он это делает и 2) как выполнить sh того, что я пытаюсь сделать? Я понимаю, что это странная установка, но я имею дело с некоторыми странными библиотеками React, и это именно то, где я сейчас нахожусь. Пожалуйста, предположите, что я должен сохранить ту же общую структуру функции, возвращающей функцию, возвращающую функцию, как показано в примере. Также я был бы очень признателен, если бы вы сохранили свой ответ как можно более простым, поскольку я немного новичок в TS. Заранее спасибо!

1 Ответ

2 голосов
/ 02 апреля 2020

Make metafunction generi c.

Как указано выше, нет универсального c типа. Чтобы быть в безопасности, firstClosure будет использовать только ключ, общий для foo и bar, но у них нет общего ключа, поэтому never является единственным возможным параметром. Если вы дадите им общий ключ, наберите firstClosure, чтобы принять это.

interface BigObject {
  foo: {
    a?: string
    b?: string
    f?: string   // Added
  }
  bar: {
    c?: string
    d?: string
    f?: string   // Added
  }
}

const instance: BigObject = {
  foo: {
    a: "a",
    b: "b",
    f: "f",
  },
  bar: {
    c: "c",
    d: "d",
    f: "f",
  }
}

const secondClosure = firstClosure("f")   // "f" is the only valid value

игровая площадка

Путем добавления универсального c, вы можете убедить Typescript сохранить информацию о типе как свойство metafunction и firstClosure каждый, что дает вашему закрытию тип, который вы ищете.

function metafunction<T extends keyof BigObject>(bigObjProp: T) {
  type LittleObject = BigObject[T]

  return function (littleObjProp: keyof LittleObject) {
    return function (bigObject: BigObject) {
      const littleObject = bigObject[bigObjProp]
      return littleObject[littleObjProp] ?? "fallback value"
    }
  }
}

площадка для ввода текста

...