为什么 Typescript 将我的 keyof 类型强制为 never 类型,我该如何解决?

Why is Typescript coercing my keyof type to a never type and how do I fix it?

抱歉,如果这是一个骗局,我是 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 会选择哪个 "sub interface" 用于任何给定的调用。因此,例如,当我调用 metafunction("foo") 时,TS 会将 LittleObject 设置为 { a?: string; b?: string },因此,当我调用 firstClosure("a") 时,它会说 "ah yes, 'a' is indeed a valid key of LittleObject, carry on"。然而,它不能这样做,因为它总是认为 keyof LittleObject 意味着 never.

有人可以帮助我理解 1) 为什么这样做以及 2) 如何完成我想要做的事情吗?我意识到这是一个奇怪的设置,但我正在处理一些奇怪的 React 库,而这正是我目前所处的位置。请假设我必须保持返回函数的函数的整体结构与示例中所见相同。另外,如果您能尽可能简单地回答我,我将不胜感激,因为我对 TS 有点陌生。提前致谢!

使 metafunction 通用。

如上所示,没有通用类型。为了安全起见,firstClosure 只会采用 foobar 共有的密钥,但它们没有公共密钥,因此 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

typescript playground

通过添加泛型,您可以说服 Typescript 将类型信息保留为 metafunctionfirstClosure 的 属性,这将为您的闭包提供您正在寻找的类型对于.

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"
    }
  }
}

typescript playground