简体   繁体   English

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

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

Sorry if this is a dupe, I'm new to TypeScript and am having trouble figuring out if similar-looking questions are related because a lot of them are doing very complex things.对不起,如果这是一个骗局,我是 TypeScript 的新手,并且无法弄清楚看起来相似的问题是否相关,因为他们中的很多人都在做非常复杂的事情。 Anyway the problem is, I have a relatively simple setup that TS is choking on because it's coercing a type to never and I don't really understand why it's doing that.无论如何,问题是,我有一个相对简单的设置,TS 正在窒息,因为它强制一种类型never ,我真的不明白它为什么这样做。 Here's the setup:这是设置:

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)

My expectation is that the value of value will be "a".我的期望是 value 的value将是“a”。

I don't understand why littleObjProp resolves to never .我不明白为什么littleObjProp解析为never My assumption would be that because LittleObject is built from the type of the argument passed in to metafunction , TS would pick which "sub interface" to use for any given invocation.我的假设是,由于LittleObject是从传递给参数的类型内置metafunction ,TS会选择使用任何给定调用其“子接口”。 So, for example, when I call metafunction("foo") , TS would set LittleObject to { a?: string; b?: string }因此,例如,当我调用metafunction("foo") ,TS 会将LittleObject设置为{ a?: string; b?: string } { a?: string; b?: string } and thus, when I call firstClosure("a") , it would say, "ah yes, 'a' is indeed a valid key of LittleObject, carry on". { a?: string; b?: string }因此,当我调用firstClosure("a") ,它会说,“啊,是的,'a' 确实是 LittleObject 的有效键,继续”。 It can't do this, however, because it always thinks that keyof LittleObject means never .然而,它不能这样做,因为它总是认为keyof LittleObject意味着never

Can someone help me understand 1) why it's doing this and 2) how to accomplish what I'm trying to do?有人可以帮助我理解 1) 为什么这样做和 2) 如何完成我正在尝试做的事情? I realize it's a weird setup but I'm dealing with some weird React libraries and this is just where I am at the moment.我意识到这是一个奇怪的设置,但我正在处理一些奇怪的 React 库,而这正是我目前所处的位置。 Please assume I have to keep the same overall structure of a function returning a function returning a function as seen in the example.请假设我必须保持一个函数的整体结构,该函数返回一个返回一个函数的函数,如示例中所示。 Also I would really appreciate it if you could keep your answer as simple as possible since I am a bit new to TS.另外,如果您能尽可能简单地回答我的问题,我将不胜感激,因为我对 TS 有点陌生。 Thanks in advance!提前致谢!

Make metafunction generic.使metafunction通用。

As you have it above, there's no generic type.正如上面所说,没有泛型类型。 To be safe firstClosure is only going to take a key that foo and bar have in common, but they have no key in common so never is the only possible parameter.为了安全起见, firstClosure只会采用foobar共有的键,但它们没有共同的键,所以never是唯一可能的参数。 If you were to give them a key in common, firstClosure would be typed to accept that.如果您要给它们一个共同的键, firstClosure键入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 打字稿游乐场

By adding a generic, you can convince Typescript to keep the type information as a property of metafunction and firstClosure each, which gives your closure the type you're looking for.通过添加泛型,您可以说服 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 打字稿游乐场

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM