【问题标题】:Narrowing generic type which is keyof type without specifying generic argument缩小作为 keyof 类型的泛型类型而不指定泛型参数
【发布时间】:2019-06-15 19:12:41
【问题描述】:

抱歉,如果这在某处重复,我的 google-fu 未能找到正确的词来缩小结果范围。

我正在尝试创建一个辅助函数来重用某些功能,但无法完全弄清楚是否/如何实现它。

给定一个具有泛型的函数(或类型),其中一个参数是指定泛型类型的键,函数返回类型被确定为泛型类型上该特定属性的返回类型,而不是泛型类型上的每个属性类型。

interface MyObject {
  readonly foo: Date;
  readonly bar: number;
  readonly wizz: string;
  readonly bang: boolean;
}

function getHelpers<ItemType>(key: keyof ItemType) {
  return (item: ItemType) => item[key];
}

const res = getHelpers<MyObject>('bang');
// Has type of `(item: MyObject) => string | number | boolean | Date`, when I need it to have `(item: MyObject) => boolean`

我知道如果我添加一个辅助泛型参数来缩小键类型,那么它就可以工作:

function getHelpers<ItemType, KeyType extends keyof ItemType>(key: KeyType) {
  return (item: ItemType) => item[key];
}

const res = getHelpers<MyObject, 'bang'>('bang'); // Has typed of `(item: MyObject) => boolean`

但如果可能的话,我宁愿不必在每次调用函数时都通过指定通用参数来复制键。

【问题讨论】:

  • 'bang' (在getHelpers&lt;MyObject&gt;('bang') 中)将永远是一个文字,对吧?从来没有运行时值?
  • 这是一个真的有趣的问题。
  • 这不是那些 UnionToIntersection 类型的案例之一吗?并不是说我有一个特定的解决方案...... :) 但它基本上是关于确保类型参数只是一个键而不是键的联合,对吧?
  • @T.J.Crowder 是的,总是一个字面意思,至少在我想做的事情上

标签: typescript


【解决方案1】:
interface MyObject {
    readonly foo: Date;
    readonly bar: number;
    readonly wizz: string;
    readonly bang: boolean;
}

不幸的是,如果没有函数柯里化,这是不可能的。您的第二个选择是正确的方法。理想情况下,我们希望指定ItemType,但推断KeyType。不幸的是,这是不可能的。我们要么推断所有类型参数,要么必须全部指定它们。有一个功能提案支持部分推理,但已经推迟了好几次,目前还没有在路线图上。

解决方案是使用两个函数调用,第一个函数我们指定ItemType,第二个调用我们让推理工作。

function getHelpers<ItemType>() {
    return function <KeyType extends keyof ItemType>(key: KeyType) {
        return (item: ItemType) => item[key];
    }
}

const res = getHelpers<MyObject>()('bang'); // Has typed of `(item: MyObject) => boolean`

【讨论】:

  • 部分推理真的是理想的解决方案吗?对我来说,这仍然看起来不干净。 理想情况下我可以将类型限制为我想要的(这正是作为文字类型的键之一,而不是任何子类型)。
  • @IngoBürk 上面的解决方案没有限制K 不是一个联合。它所做的是根据参数类型推断K。没有内置的oneof 约束,尽管已经提出。不允许联合是可能的,但需要像您在UnionToIntersection 中暗示的那样。 OP 最初有一个联合的原因是没有对密钥进行推断。所以我们想要推断出的密钥。但同时,由于无法从中推断项目类型,我们希望手动指定它。所以是的,我认为,需要部分推断才能在一次调用中输入它。
  • @IngoBürk 忘了说,我们想要推断出的密钥,因为我们需要在不推断的情况下将最内层函数的返回键入为ItemType[KeyType](即推断出的函数的返回类型)返回类型的关键是ItemType[keyof Item Type]
  • 谢谢!我已经看到几个库具有相同的模式,具有泛型函数并返回一个函数以进一步调用,但并没有将它们放在一起,因为它是为了解决与我所拥有的相同的问题!
猜你喜欢
  • 2020-11-11
  • 1970-01-01
  • 2018-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-28
相关资源
最近更新 更多