我创建了一个 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<{}>
而不是 Maybe<T[K]>
?
最佳答案
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
实际上可以是 any number | 类型的键 |字符串 |符号
。因为如果 T extends Dict
,这并不意味着它不能有任何其他键,如“lastname”等。换句话说:
type IsDict = {name: string, age: number, lastname: string} extends Dict ? true : false // IsDict is true
看到了吗?您的代码将允许使用 lastname
键,而 Typescript 会保护您不这样做。
也就是说, typescript 目前 does not have将类型参数限制为精确类型的方法(有 workarounds 但遗憾的是它们不适合您的情况)。
关于具有索引类型的 TypeScript 通用柯里化(Currying)函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53824319/