Flow 如何解释泛型类型?

use*_*804 3 flowtype

我想了解 Flow 如何决定泛型类型使用什么类型,以及是否有办法控制推断泛型类型的级别(我的意思是进一步解释)。

这个问题的灵感来自How to type a generic function that returns subtypes。我认为这两个问题是有区别的,因为这个问题侧重于理解如何T选择,而链接则侧重于键入函数的返回类型。

身份函数是一个很好的剖析示例。它的类型相当简单

function identity<T>(value: T): T;
Run Code Online (Sandbox Code Playgroud)

这似乎足以了解实现应该是什么。但是,我觉得这种类型不足以知道身份函数实际上是做什么的。例如,我们可以(正如链接的问题试图做的那样),

function identity<T>(value: T): T {
  if (typeof value === 'string') {
    return '';
  }

  return value;
}
Run Code Online (Sandbox Code Playgroud)

尝试流程

这不会进行类型检查,Flow 会抱怨返回空字符串。然而,我想在许多语言中这会很好——我们在输入astring时返回 a string,否则我们将返回value类型的原始值——T但出于某种原因 Flow 不喜欢这样。

这个答案让我的困惑更加复杂,我们可以返回value.substr(0, 0)而不是空字符串并且 Flow 将不再抱怨,并且无法返回严格相等的值,

function identity<T>(value: T): T {
  if (value === '') {
    return '';
  }

  return value;
}
Run Code Online (Sandbox Code Playgroud)

尝试流程

我认为造成这种差异的一个主要原因是,除了“JavaScript 类型”之外,文字还可以像 Flow 中的类型一样。例如,

const x: 5 = 5; // literal type
const x: number = 5; // JavaScript type
Run Code Online (Sandbox Code Playgroud)

都是有效的。然而,这意味着当我们有一个 type 的函数时T => T,我们不知道 Flow 是将文字类型还是 JavaScript 类型推断为类型。

我想知道是否有某种方法可以知道 Flow 对函数中的泛型类型推断出什么,或者是否有办法将泛型类型的范围限定在“文字”级别或“JavaScript”级别。有了这种能力,我们就可以键入将值强制为该类型的默认值的函数(即,字符串将变为空字符串,数字将变为 0)。这里函数的类型实际上是T => T,但希望可以防止 Flow 抱怨返回默认值。

Lyl*_*ood 6

如果没有直接回答问题,希望在这里能说明正在发生的事情。

让我们首先举你的第一个例子:

function identity<T>(value: T): T {
  if (typeof value === 'string') {
    return '';
  }

  return value;
}
Run Code Online (Sandbox Code Playgroud)

函数签名是identity<T>(T): T. 这基本上是说:

  1. 我们正在创建一个新类型T,它可以是任何东西 ( <T>)。
  2. 我们的函数将接收一个类型为 的参数T
  3. 我们的函数将返回一个 type 值T

从现在开始,这些限制都不会改变,类型T也不会改变。identity必须返回 的确切类型T,而不是其类型的子集。让我们来看看为什么。

identity<'some string'>('some string');
Run Code Online (Sandbox Code Playgroud)

在这种情况下,类型T是文字类型,'some string'。在调用上述函数的情况下,我们会发现typeof value === 'string'并尝试返回''a stringstring但是,是的超类型T'some string',所以我们侵犯了该函数的合同。

在简单字符串的情况下,这一切似乎相当人为,但实际上是必要的,并且在扩展到更复杂的类型时更加明显。

让我们看看我们奇怪的身份函数的正确实现:

function identity<T>(value: T): T | string {
  if (typeof value === 'string') {
    return '';
  }

  return value;
}
Run Code Online (Sandbox Code Playgroud)

的返回类型T只能由完全匹配的东西来满足T,在我们的签名的情况下只能是value。然而,我们有一个特殊的情况,其中identity可能返回 a string,所以我们的返回类型应该是T | string(或者,如果我们想要超级特定,T | '') 的联合。

现在让我们继续第二个例子:

function identity<T>(value: T): T {
  if (value === '') {
    return '';
  }

  return value;
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,流只是不支持value === ''作为一种细化机制。流程中的细化非常挑剔,我喜欢将其视为在我的代码上运行的一些简单正则表达式的列表。实际上只有将类型细化为字符串的方法,那就是使用typeof value === 'string'. 其他比较不会细化到字符串。提炼泛型肯定也​​有一些奇怪的地方,但像这样的东西工作正常(当然,提炼确实如此,它仍然表现出以前与泛型相关的错误):

function identity<T>(value: T): T {
  if (typeof value === 'string' && (value: string) === '') {
    return '';
  }

  return value;
}
Run Code Online (Sandbox Code Playgroud)

尝试

至于这个substr例子,对我来说这绝对是一个错误。似乎您可以对任何String返回 a 的方法执行相同的操作string,例如concator slice

我想知道是否有某种方法可以知道 Flow 对函数中的泛型类型推断出什么

在函数体流中并没有真正推断出泛型的类型。泛型有一个具体的定义(Tis T,本质上是一个未知类型,除非它有边界,在这种情况下,它是一个与这些边界匹配的未知类型)。Flow 可以推断进入函数调用的参数类型,但这应该与函数的编写方式无关。

或者是否有办法将泛型类型的范围限定在“文字”级别或“JavaScript”级别。有了这种能力,我们就可以键入将值强制为该类型的默认值的函数(即,字符串将变为空字符串,数字将变为 0)。这里函数的类型实际上是 T => T,但希望可以防止 Flow 抱怨返回默认值。

这里的问题是这将不再是T => T. 正如我上面所展示的,破坏这样的实现是微不足道的。