我以前从未用静态类型语言编写过。我主要使用 Javascript 进行开发,最近我有兴趣了解更多有关 FB 的 Flowtype 的信息。
我发现文档写得很好,而且我理解了其中的大部分内容。但是我不太明白 generics 的概念.我试过用谷歌搜索一些例子/解释,但没有成功。
谁能解释一下什么是泛型,它们主要用于什么,并可能提供一个例子?
最佳答案
假设我想编写一个只存储单个值的类。显然这是人为的;我保持简单。实际上这可能是一些集合,比如 Array
, 可以存储多个值。
假设我需要包装一个 number
:
class Wrap {
value: number;
constructor(v: number) {
this.value = v;
}
}
现在我可以创建一个存储数字的实例,并且可以取出该数字:
const w = new Wrap(5);
console.log(w.value);
到目前为止一切顺利。但是等等,现在我还想包装一个 string
!如果我天真地尝试包装一个字符串,我会得到一个错误:
const w = new Wrap("foo");
给出错误:
const w = new Wrap("foo");
^ string. This type is incompatible with the expected param type of
constructor(v: number) {
^ number
这行不通,因为我告诉 Flow Wrap
只需 numbers
.我可以重命名 Wrap
至 WrapNumber
,然后复制它,调用副本WrapString
, 并更改 number
至 string
体内。但这很乏味,现在我要维护同一件事的两个副本。如果我每次想包装新类型时都继续复制,这很快就会失控。
但请注意 Wrap
实际上并不在 value
上运行.它不关心它是否是number
或 string
, 或者是其他东西。它的存在只是为了存储它并在以后归还它。这里唯一重要的不变量是你给它的值和你取回的值是同一类型。使用什么特定类型并不重要,只要这两个值具有相同的值即可。
因此,考虑到这一点,我们可以添加一个类型参数:
class Wrap<T> {
value: T;
constructor(v: T) {
this.value = v;
}
}
T
这里只是一个占位符。它的意思是“我不在乎你放在这里的是什么类型,但重要的是到处都使用 T
,它是相同的类型。”如果我给你一个Wrap<number>
您可以访问 value
属性并知道它是 number
.同样,如果我给你一个 Wrap<string>
你知道 value
对于那个例子是string
.使用此新定义 Wrap
,让我们再次尝试包装 number
和一个 string
:
function needsNumber(x: number): void {}
function needsString(x: string): void {}
const wNum = new Wrap(5);
const wStr = new Wrap("foo");
needsNumber(wNum.value);
needsString(wStr.value);
Flow 推断类型参数并且能够理解这里的一切都将在运行时工作。如果我们尝试这样做,我们也会像预期的那样得到一个错误:
needsString(wNum.value);
错误:
20: needsString(wNum.value);
^ number. This type is incompatible with the expected param type of
11: function needsString(x: string): void {}
^ string
(完整示例为 tryflow)
关于javascript - 使用 Javascript 的 Flowtype 解释泛型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44809690/