Ramda 的 Flow 类型中的 Curried 函数定义
Curried function definition in Ramda's Flow typings
Ramda 的流类型有以下curried function definitions:
declare type __CurriedFunction1<A, R, AA: A> =
& ((...r: [AA]) => R)
declare type CurriedFunction1<A, R> = __CurriedFunction1<A, R, *>
declare type __CurriedFunction2<A, B, R, AA: A, BB: B> =
& ((...r: [AA]) => CurriedFunction1<BB, R>)
& ((...r: [AA, BB]) => R)
declare type CurriedFunction2<A, B, R> = __CurriedFunction2<A, B, R, *, *>
// ...
我不明白 ____CurriedFunctionN
助手和 AA
、BB
类型的需要,即为什么 CurriedFunction1<A, R>
不能定义为:
declare type CurriedFunction1<A, R> =
& ((...r: [A]) => R)
这与类型差异有关。如果我说我一开始也不理解它,并解释我是如何尝试理解它的,这可能更有意义。
在类型的定义中,唯一的区别是AA: A
表示"AA is exactly or a subtype of A"。所以我假设原因与如何将子类型视为函数的参数有关。
所以我重新创建了一个简单的示例,使用您的类型的更简单版本并将子类型作为参数传递:
declare type MyCurriedFunction1<A, R> =
& ((...r: [A]) => R)
let plus2: MyCurriedFunction1<number, number> = (x) => x+2;
let y: 1 | 2 = 1;
plus2(x);
果然,这会抛出一个错误:
18: let plus2: MyCurriedFunction1<number, number> = (x) => x+2;
^ number. This type is incompatible with
21: plus2(x);
^ number enum (1 | 2)
Flow 告诉我们,对于函数类型的这个更简单的版本,它不能接受比用 (number
) 定义的参数更具体的类型 (1 | 2
)。这与称为类型方差的想法有关,它是关于我们何时可以安全地使用子(或超)类型来代替彼此。维基百科有一个 sort-of-readable explanation 为什么 array/tuple 类型通常是不变的:元组可以被读取(这将允许协变)或写入(这将允许逆变),所以为了这两种可能性的安全,你可以只使用完全相同的类型。
不过,在这种情况下,我们希望允许我们的函数采用其参数类型的子类型似乎很清楚。默认情况下,[A]
不会发生这种情况,它是参数的一个参数类型的元组,因此我们必须明确允许对参数使用子类型。
因此,通过像 Ramda 那样声明类型,我们现在可以将子类型作为参数传递给使用此类型的函数。
declare type __CurriedFunction1<A, R, AA: A> =
& ((...r: [AA]) => R)
declare type CurriedFunction1<A, R> = __CurriedFunction1<A, R, *>
let plus1: CurriedFunction1<number, number> = (x) => x+1;
let x: 1 | 2 = 1;
plus1(x);
而且我们没有收到任何类型错误!
Ramda 的流类型有以下curried function definitions:
declare type __CurriedFunction1<A, R, AA: A> =
& ((...r: [AA]) => R)
declare type CurriedFunction1<A, R> = __CurriedFunction1<A, R, *>
declare type __CurriedFunction2<A, B, R, AA: A, BB: B> =
& ((...r: [AA]) => CurriedFunction1<BB, R>)
& ((...r: [AA, BB]) => R)
declare type CurriedFunction2<A, B, R> = __CurriedFunction2<A, B, R, *, *>
// ...
我不明白 ____CurriedFunctionN
助手和 AA
、BB
类型的需要,即为什么 CurriedFunction1<A, R>
不能定义为:
declare type CurriedFunction1<A, R> =
& ((...r: [A]) => R)
这与类型差异有关。如果我说我一开始也不理解它,并解释我是如何尝试理解它的,这可能更有意义。
在类型的定义中,唯一的区别是AA: A
表示"AA is exactly or a subtype of A"。所以我假设原因与如何将子类型视为函数的参数有关。
所以我重新创建了一个简单的示例,使用您的类型的更简单版本并将子类型作为参数传递:
declare type MyCurriedFunction1<A, R> =
& ((...r: [A]) => R)
let plus2: MyCurriedFunction1<number, number> = (x) => x+2;
let y: 1 | 2 = 1;
plus2(x);
果然,这会抛出一个错误:
18: let plus2: MyCurriedFunction1<number, number> = (x) => x+2;
^ number. This type is incompatible with
21: plus2(x);
^ number enum (1 | 2)
Flow 告诉我们,对于函数类型的这个更简单的版本,它不能接受比用 (number
) 定义的参数更具体的类型 (1 | 2
)。这与称为类型方差的想法有关,它是关于我们何时可以安全地使用子(或超)类型来代替彼此。维基百科有一个 sort-of-readable explanation 为什么 array/tuple 类型通常是不变的:元组可以被读取(这将允许协变)或写入(这将允许逆变),所以为了这两种可能性的安全,你可以只使用完全相同的类型。
不过,在这种情况下,我们希望允许我们的函数采用其参数类型的子类型似乎很清楚。默认情况下,[A]
不会发生这种情况,它是参数的一个参数类型的元组,因此我们必须明确允许对参数使用子类型。
因此,通过像 Ramda 那样声明类型,我们现在可以将子类型作为参数传递给使用此类型的函数。
declare type __CurriedFunction1<A, R, AA: A> =
& ((...r: [AA]) => R)
declare type CurriedFunction1<A, R> = __CurriedFunction1<A, R, *>
let plus1: CurriedFunction1<number, number> = (x) => x+1;
let x: 1 | 2 = 1;
plus1(x);
而且我们没有收到任何类型错误!