【问题标题】:TypeScript generic curried function with index type具有索引类型的 TypeScript 通用柯里化函数
【发布时间】:2018-12-18 09:37:10
【问题描述】:

我创建了一个 TypeScript curried 函数,它首先接收属性名称作为字符串,然后接收对象以从中获取该属性的值。 我使用了索引类型来确保每当我尝试访问不存在的属性时都会收到错误:

export interface Dict {
  name: string;
  age: number;
}

const prop = <T extends Dict, K extends keyof T>(p: K) => (obj: T): T[K] => obj[p];

prop('name')({name: 'John', age: 45});  // John
prop('name2')({name: 'John', age: 45});  // error...

最后一行给出了错误:

error TS2345: Argument of type '"name2"' is not assignable to parameter of type '"name" | "age"'.

这正是我想要的,因为属性 name2 在作为第二个参数给出的对象上不存在。

但是,当我尝试使用 Maybe monad 创建安全版本时,它会给出类似的错误:

const safeProp = <T extends Dict, K extends keyof T>(p: K) => (obj: T): Maybe<{}> => compose2(Maybe.of, prop(p))(obj);

错误出现在compose2函数的第二个参数上:prop(p)

Argument of type 'K' is not assignable to parameter of type '"name" | "age"'.

我不明白,因为我声明了 K extends keyof T,我认为这是正确的,因为它也适用于 prop 函数。

供参考,compose2 函数:

const compose2 = <A, B, C>(f: (b: B) => C, g: (a: A) => B): ((a: A) => C) => a => f(g(a));

以及Maybe monad 的相关部分:

class Maybe<A> {
  static of<A>(x: A): Maybe<A> {
    return new Maybe(x);
  }

  ...
}

如何正确键入safeProp 函数,为什么我需要将其返回类型指定为Maybe&lt;{}&gt; 而不是Maybe&lt;T[K]&gt;

【问题讨论】:

    标签: typescript functional-programming


    【解决方案1】:

    an issue here 详细说明了 Typescript 在将泛型函数传递给泛型高阶函数时如何无法正确推断类型(在您的情况下,就是将 Maybe.of&lt;A&gt; 传递给 compose2&lt;A,B,C&gt;)。

    您可以通过在使用时手动填写compose2 的类型来缓解这种情况。

    const safeProp = <T extends Dict, K extends keyof T>(p: K) => (obj: T) => compose2<T, T[K], Maybe<T[K]>>(Maybe.of, prop(p))(obj);
    

    safeProp 现在具有正确的类型 const safeProp: &lt;T extends Dict, K extends keyof T&gt;(p: K) =&gt; (obj: T) =&gt; Maybe&lt;T[K]&gt;

    【讨论】:

    • 很好的答案,谢谢。为了完整性,我还添加了返回类型:const safeProp3 = &lt;T extends Dict, K extends keyof T&gt;(p: K) =&gt; (obj: T): Maybe&lt;T[K]&gt; =&gt; compose2&lt;T, T[K], Maybe&lt;T[K]&gt;&gt;(Maybe.of, prop(p))(obj);
    • 最近为 TypeScript 合并了一个 PR,这可能会在未来为这种情况提供更好的推理:github.com/Microsoft/TypeScript/pull/30215
    • 太好了,谢谢!明天我会好好看看,但我猜这些是在即将到来的 TypeScript 3.4 版本中传播的泛型类型参数? devblogs.microsoft.com/typescript/announcing-typescript-3-4-rc
    【解决方案2】:
    const safeProp = <T extends Dict, K extends keyof Dict>(p: K) => (obj: T): Maybe<{}> => compose2(Maybe.of, prop(p))(obj);
    

    上面的代码没有给您错误,我将尝试解释原因(以及您的代码为什么会这样)。

    K extends keyof Dict 正好是 nameage(这只是函数 prop 允许的参数)。和 K extends keyof T 真的可以是number | string | Symbol 类型的任何键。因为如果T extends Dict,并不意味着它不能有任何其他键,如“姓氏”等。换句话说:

    type IsDict = {name: string, age: number, lastname: string} extends Dict ? true : false // IsDict is true
    

    看到了吗?您的代码将允许 lastname 键,Typescript 会保护您不这样做。

    也就是说,typescript 目前 does not have 是将类型参数限制为 exact 类型的方法(有 workarounds 但不幸的是它们不适合您的情况)。

    【讨论】:

    • 很好的解释,非常感谢!不过,我确实接受了 y2bd 的回答,因为它更加完整并且允许指示正确的返回类型。
    • @DannyMoerkerke 好的,不客气!但问题不在于prop 函数吗?我认为Maybe 和东西无关紧要,问题是prop(p) 被错误突出显示。
    • 是的,主要问题出在prop 函数上,但就像我说的,我也想知道为什么返回类型是Maybe&lt;{}&gt;。为compose2 函数指定类型为我解释了一切。
    猜你喜欢
    • 1970-01-01
    • 2020-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-01
    • 2022-11-25
    • 2018-02-05
    • 1970-01-01
    相关资源
    最近更新 更多