如何在Flow中使用相同泛型类型的值对泛型函数进行类型检查

时间:2018-07-23 20:45:49

标签: javascript reactjs generics flowtype

我们将Flow与React结合使用,并编写了这个DropDown包装器,它可以为我们做一些事情。

直到现在,该下拉列表仅需要使用number id,但是最近,我们还必须添加对string id的支持。

不想重写所有内容,我们考虑在道具中使用通用类型参数,以便id(和onChange处理程序)将使用该通用类型。

但是,我们似乎无法正确使用该更改处理程序。尝试调用处理程序时,我们会收到关于要发送的值的类型(numberstring)的Flow错误,告诉我们该类型与T不兼容:

Cannot call 'handler' with 'value' bound to 'value' because string [1] is incompatible with 'T'

number调用相同。

我们相信,如果我们成功检查一个通用值的类型,它将使用我们道具中相同的通用类型来折叠每个元素的类型。他们在这里:

type Props<T : string | number> = {
  options?: Array<{id: T, displayValue: string}>,
  onValueChange?: (value: T | null) => mixed,
}

因为我们知道Flow不允许使用带有onValueChange的道具来创建元素,如果number包含options的ID,则string会处理{例如,我们认为通过检查options中id的类型,我们也可以将T的{​​{1}}折叠起来。

这是我们得到错误的地方:

onValueChange

这样,我的问题是:是否可以确定函数参数的泛型,以便我们可以按需使用它而不会出错?

如果答案是肯定的,那么我们的代码在做什么,阻止类型检查按需进行?

如果答案是否定的,还有什么比将我们的值转换为export class DropDown<T : string | number> extends React.Component<Props<T>, {}> { handleChange = (event: {target: {value: string}}) => { if (!this.props.onValueChange) return const handler = this.props.onValueChange const firstElement = this.props.options && this.props.options[0] if (!firstElement) return const value = event.target.value // I don't understand why, but this if-else if generates an error. if (typeof firstElement.id === 'string') { handler(value) } else if (typeof firstElement.id === 'number') { const num = parseInt(value) handler(num) } } } 来使我们的处理程序接受它更好的方法了?

我在此Flow Try snippet中编写了一个最小的工作示例,因此您可以看到实时错误以及预期的用法(请注意,用法部分中的错误是预期的:它们在那里显示使用通用组件就可以按我们希望的方式进行工作,即使使用值也不行)。重要的错误是错误输出中的前两个。

提前感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

感谢这个非常有趣的问题! 不幸的是,我认为现在无法通过优化数组中第一个元素的类型来优化类型T。当我们使用以下语句时可以看到这一点:

if (firstElement) {
  if (typeof firstElement.id === 'string') {
    const refined: string = firstElement.id;
    const notRefined: T = refined; // Errors
    const alwaysWorks: T = firstElement.id;
  }
  const alwaysWorks: T = firstElement.id;
}

第二次分配错误。有趣的是,这也可以通过将第一个赋值标记为T来解决,这表明Flow的工作方式:Flow本身并不精简类型,而是精简值的类型标签。而且我认为您已经正确地得出结论,您需要做的是完善回调函数的值。但这是行不通的,因为我们无法对函数进行任何运行时检查(可能要获取其参数数量除外)。

因此,我们了解到Flow不够好。当您更深入地使用Flow时,您可能会不时注意到这一点:很难表达您想做的事情。我不确定Flow是否出错,或者只是很难键入的JavaScript。无论如何,作为一种解决方案,我建议您仅使用Function注释处理程序变量。函数在某种程度上可以是任何类型,并使函数更通用。我们知道代码可以工作(请参见我的更正,进行空检查,然后检查id属性而不是对象),并且外部接口非常明确地说明了类型。有时,重要的是API,在内部,我们不得不做一点修改。很好(例如,我们可以在热门项目like these 13 cases in graphql-js中看到很多东西)

奖金:如果您喜欢函数式编程,请查看this library如何处理类型,以创建Maybe类型的幻觉,而这种幻觉没有运行时开销,因为它实际上只是null

答案 1 :(得分:0)

我认为您正在考虑问题。似乎在将event.target.value的类型声明为“字符串”而不是“ T”时犯了一个小错误。

尝试一下

export class DropDown<T : string | number> extends React.Component<Props<T>, {}> {
  handleChange = (event: {target: {value: T}}) => { // not {value: string}
    if (!this.props.onValueChange) return
    const handler = this.props.onValueChange
    const value = event.target.value
    handler(value)
  }
}  

我希望这会对您有所帮助。