我创建了一个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]>
?
答案 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
恰好是name
或age
(它们仅是函数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>