我以前从未用静态类型语言写过。我主要是用Javascript开发的,最近我一直有兴趣了解更多有关FB的Flowtype的信息。
我发现文档编写得很好,我理解其中的大部分内容。但是我不太明白generics的概念。我试过谷歌搜索一些例子/解释,但没有运气。
有人可以解释什么是仿制药,它们主要用于什么,也许可以提供一个例子?
答案 0 :(得分:6)
让我们说我想写一个只存储单个值的类。显然这是做作的;我保持简单。实际上,这可能是一些集合,如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
这里只是一个占位符。这意味着&#34;我不在乎你在这里放置什么类型,但重要的是,无论使用T
,它都是相同的类型。&#34;如果我通过了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推断出type参数,并且能够理解这里的所有内容都可以在运行时运行。如果我们尝试这样做,我们也会出现预期的错误:
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为完整示例)
答案 1 :(得分:5)
静态类型语言中的泛型是一种定义单个函数或类的方法,可以应用于任何类型依赖项,而不是为每种可能的数据类型编写单独的函数/类。它们确保一个值的类型始终在分配给相同通用值的另一个类型中相同。
例如,如果您想编写一个将两个参数组合在一起的函数,那么该操作(取决于语言)可能完全不同。在JavaScript中,因为它不是一个静态类型的语言,所以无论如何你都可以这样做并在函数中键入check,但是Facebook Flow
除了单个定义之外还允许类型一致性和验证。
function add<T>(v1: T, v2: T): T {
if (typeof v1 == 'string')
return `${v1} ${v2}`
else if (typeof v1 == 'object')
return { ...v1, ...v2 }
else
return v1 + v2
}
在这个例子中,我们定义了一个泛型T
的函数,并说所有参数都是相同的类型T
,函数将始终返回< em>相同类型T
。在函数内部,因为我们知道参数将始终是相同的类型,我们可以使用标准JavaScript测试其中一个的类型并返回我们感知的内容和&#34;添加&#34;对于那种类型。
稍后在我们的代码中使用时,可以将此函数调用为:
add(2, 3) // 5
add('two', 'three') // 'two three'
add({ two: 2 }, { three: 3 }) // { two: 2, three: 3 }
但如果我们尝试,会抛出打字错误:
add(2, 'three')
add({ two: 2 }, 3)
// etc.
答案 2 :(得分:2)
基本上,它只是一个类型的占位符。
当使用泛型类型时,我们说这里可以使用任何Flow类型。
通过在函数参数之前放置<T>
,我们说这个函数可以(但不必)在其参数列表,其正文和任何位置使用泛型类型T
。作为其返回类型。
让我们看看他们的基本例子:
function identity<T>(value: T): T {
return value;
}
这意味着value
中的参数identity
将具有某种类型,这是事先不知道的。无论该类型是什么,identity
的返回值也必须与该类型匹配。
const x: string = identity("foo"); // x === "foo"
const y: string = identity(123); // Error
考虑泛型的一种简单方法是想象一种原始类型而不是T
,看看它是如何工作的,然后理解这种原始类型可以替代任何其他类型。
就identity
而言:将其视为接受[string]并返回[string]的函数。然后理解[string]也可以是任何其他有效的流类型。
这意味着identity
是一个接受T
并返回T
的函数,其中T
是任何流类型。
文档也有这个有用的类比:
泛型类型与变量或函数参数非常相似,只是它们用于类型。
注意:此概念的另一个词是polymorphism。