索引类型为

时间:2018-12-17 23:13:54

标签: typescript functional-programming

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

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<{}>而不是Maybe<T[K]>

2 个答案:

答案 0 :(得分:1)

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

您可以通过在使用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: <T extends Dict, K extends keyof T>(p: K) => (obj: T) => Maybe<T[K]>

答案 1 :(得分:1)

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,则并不意味着它不能具有“ lastname”等其他任何键。换句话说:

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

看到了吗?您的代码将允许使用lastname键,而Typescript可防止您这样做。

也就是说,打字稿目前does not have是一种将类型参数限制为 exact 类型的手段(有workarounds,但不幸的是,它们不适合您的情况)。 / p>