首页 > 解决方案 > Flow 如何解释泛型类型?

问题描述

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

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

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

function identity<T>(value: T): T;

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

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

  return value;
}

尝试流

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

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

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

  return value;
}

尝试流

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

const x: 5 = 5; // literal type
const x: number = 5; // JavaScript type

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

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

标签: flowtype

解决方案


如果不直接回答问题,希望在这里对发生的事情有所了解。

首先让我们以您的第一个示例为例:

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

  return value;
}

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

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

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

identity<'some string'>('some string');

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

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

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

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

  return value;
}

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

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

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

  return value;
}

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

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

  return value;
}

试试

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

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

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

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

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


推荐阅读