Flow不允许我将`Array< A>`传递给`Array< A |。 B>`(子类型的数组到超类型的数组) [英] Flow doesn’t let me pass `Array<A>` to `Array<A | B>` (array of subtypes to array of supertypes)

查看:63
本文介绍了Flow不允许我将`Array< A>`传递给`Array< A |。 B>`(子类型的数组到超类型的数组)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的值类型为 Array< A> (子类型的数组)。 Flow不允许我将其传递到期望 Array< A | B> (超类型的数组),即使它显然可以工作。



例如,我不能为类型 Array<'left'| 'right'> 到类型为 Array< string> 的变量:

  const方向:Array<'left'| ‘正确’> = [‘右’,‘左’]; 
const消息:Array< string> =方向; //错误

会引发此错误:



< pre $ = lang-none prettyprint-override> 2:const消息:Array< string> =方向; //错误
^无法将方向分配给消息,因为在数组元素中:字符串[1]与字符串文字 left [2]不兼容。或字符串[1]与字符串文字`right` [3]不兼容。
参考:
2:const消息:Array< string> =方向; //错误
^ [1]
1:const方向:Array<’left’| ‘正确’> = [‘右’,‘左’];
^ [2]
1:const方向:Array<’left’| ‘正确’> = [‘右’,‘左’];
^ [3]

尝试流演示



类似地,我无法通过一个 Array< ANode> 转换为使用 Array< Node> 的函数,即使 Node ANode | BNode

 类型的ANode = {type:'a',value:string}; 
type BNode = {类型: b,计数:数字};

类型Node = ANode | BNode;

函数getFirstNodeType(nodes:Array< Node>):字符串{
返回节点[0] .type;
}

//有效
const节点超类型:Array< Node> = [{类型:‘a’,值:‘foo’}];
getFirstNodeType(nodesSupertype);

//错误
常量节点子类型:Array< ANode> = [{类型:‘a’,值:‘foo’}];
getFirstNodeType(nodesSubtype); //错误



  16: getFirstNodeType(nodesSubtype); //错误
^无法将 nodesSubtype绑定到 nodes的 getFirstNodeType调用,因为属性 value在 BNode [1]中丢失,但在 ANode [2]中存在于数组元素中。
参考:
6:函数getFirstNodeType(nodes:Array< Node>):字符串{
^ [1]
15:const nodesSubtype:Array< ANode> = [{类型:‘a’,值:‘foo’}];
^ [2]

16:getFirstNodeType(nodesSubtype); //错误
^无法将 nodesSubtype绑定到 nodes的 getFirstNodeType调用,因为字符串文字 a [1]与数组元素属性 type中的字符串文字 b [2]不兼容。
参考:
1:类型ANode = {类型: a,值:字符串};
^ [1]
2:类型BNode = {类型: b,计数:数字};
^ [2]

不妨试试流程演示


解决方案

流程会引发错误,因为它认为您可能会更改数组

  //给出了问题第二个示例中的`Node`,`ANode`和`BNode`的定义。

函数getFirstNodeType(nodes: Array< Node>):字符串{
const bNode:BNode = {type:'b',count:0}
//修改参数`nodes'。根据其类型,添加 BNode是有效的。
nodes.push(bNode);

返回节点[0] .type;
}

const nodes子类型:Array< ANode> = [{类型:‘a’,值:‘foo’}];
getFirstNodeType(nodesSubtype); //错误

尝试流演示



运行上面的代码后,即使声明为 ANode 的数组,nodesSubtype 仍将包含 BNode



有两种解决方案可以使Flow确信您不会改变数组。最明确的方法是用流实用程序类型替换 Array / arrays /#toc-readonlyarray rel = nofollow noreferrer> $ ReadOnlyArray

  function getFirstNodeType(nodes:$ ReadOnlyArray< Node>):string {//使用$ ReadOnlyArray 
返回节点[0 ]。类型;
}

const nodes子类型:Array< ANode> = [{类型:‘a’,值:‘foo’}];
getFirstNodeType(nodesSubtype); //没有错误

不妨试试流程演示



您只需要进行的功能参数的那个更换类型(例如 nodes ),但是如果您愿意,可以在所有适用的地方使用 $ ReadOnlyArray (例如 nodesSubtype )。



$ ReadOnlyArray 是最安全的类型,但是您可能不需要更改所有现有功能即可使用它。在这种情况下,您的替代方法是通过 any 转换数组类型。是的,您从一开始就可以做到,但至少现在您知道为什么为什么进行此转换是安全的。您甚至可以发表评论,说明为什么存在此强制转换,这可能有助于您了解不再有效的假设。

  function getFirstNodeType(nodes:Array< Node>):字符串{
return nodes [0] .type;
}

const nodes子类型:Array< ANode> = [{类型:‘a’,值:‘foo’}];
//将Array< ANode>强制转换为Array< Node>是可以的,因为我们知道getFirstNodeType不会实际修改数组; Node>));



您不关心$ 注释,仅转换为任何

  getFirstNodeType((nodesSubtype:any)); 



尝试流程演示



其他资源




I have a value of type Array<A> (an array of subtypes). Flow doesn’t let me pass it to a place that expects Array<A | B> (an array of supertypes), even though it obviously works.

For example, I can’t assign a value with type Array<'left' | 'right'> to a variable whose type is Array<string>:

const directions: Array<'left' | 'right'> = ['right', 'left'];
const messages: Array<string> = directions; // error

This error is raised:

2: const messages: Array<string> = directions; // error
                                   ^ Cannot assign `directions` to `messages` because in array element: Either string [1] is incompatible with string literal `left` [2]. Or string [1] is incompatible with string literal `right` [3].
    References:
    2: const messages: Array<string> = directions; // error
                             ^ [1]
    1: const directions: Array<'left' | 'right'> = ['right', 'left'];
                               ^ [2]
    1: const directions: Array<'left' | 'right'> = ['right', 'left'];
                                        ^ [3]

Try Flow demo

Similarly, I can’t pass an Array<ANode> to a function that takes Array<Node>, even though Node is ANode | BNode:

type ANode = {type: 'a', value: string};
type BNode = {type: 'b', count: number};

type Node = ANode | BNode;

function getFirstNodeType(nodes: Array<Node>): string {
    return nodes[0].type;
}

// works
const nodesSupertype: Array<Node> = [{type: 'a', value: 'foo'}];
getFirstNodeType(nodesSupertype);

// error
const nodesSubtype: Array<ANode> = [{type: 'a', value: 'foo'}];
getFirstNodeType(nodesSubtype); // error

16: getFirstNodeType(nodesSubtype); // error
                    ^ Cannot call `getFirstNodeType` with `nodesSubtype` bound to `nodes` because property `value` is missing in `BNode` [1] but exists in `ANode` [2] in array element.
References:
6: function getFirstNodeType(nodes: Array<Node>): string {
                                            ^ [1]
15: const nodesSubtype: Array<ANode> = [{type: 'a', value: 'foo'}];
                                ^ [2]

16: getFirstNodeType(nodesSubtype); // error
                    ^ Cannot call `getFirstNodeType` with `nodesSubtype` bound to `nodes` because string literal `a` [1] is incompatible with string literal `b` [2] in property `type` of array element.
References:
1: type ANode = {type: 'a', value: string};
                        ^ [1]
2: type BNode = {type: 'b', count: number};
                        ^ [2]

Try Flow demo

解决方案

Flow raises an error because it thinks you might mutate the array:

// given the definitions of `Node`, `ANode`, and `BNode` from the question’s second example

function getFirstNodeType(nodes: Array<Node>): string {
    const bNode: BNode = {type: 'b', count: 0}
    // Mutate the parameter `nodes`. Adding a `BNode` is valid according its type.
    nodes.push(bNode);

    return nodes[0].type;
}

const nodesSubtype: Array<ANode> = [{type: 'a', value: 'foo'}];
getFirstNodeType(nodesSubtype); // error

Try Flow demo

After running the above code, nodesSubtype would contain a BNode even though it is declared as an array of ANode, violating its type.

There are two solutions to convince Flow that you won’t mutate the array. The clearest one is to replace Array with the Flow utility type $ReadOnlyArray.

function getFirstNodeType(nodes: $ReadOnlyArray<Node>): string { // use $ReadOnlyArray
    return nodes[0].type;
}

const nodesSubtype: Array<ANode> = [{type: 'a', value: 'foo'}];
getFirstNodeType(nodesSubtype); // no error

Try Flow demo

You only have to make that replacement in the function parameter’s type (e.g. nodes), but if you liked you could use $ReadOnlyArray everywhere it applies (e.g. for nodesSubtype).

$ReadOnlyArray is the most type-safe solution, but you may not want to have to change all your existing functions to use it. In that case, your alternative is to cast the array type through any instead. Yes, you could have done that from the beginning, but at least now you know why it’s safe to do this cast. You can even leave a comment explaining why this cast exists, which might help you catch assumptions no longer being valid.

function getFirstNodeType(nodes: Array<Node>): string {
    return nodes[0].type;
}

const nodesSubtype: Array<ANode> = [{type: 'a', value: 'foo'}];
// casting `Array<ANode>` to `Array<Node>` is okay because we know that `getFirstNodeType` won’t actually modify the array
getFirstNodeType(((nodesSubtype: any): Array<Node>));

Try Flow demo

If you don’t care about documenting the problem, you can omit the comment and only cast to any:

getFirstNodeType((nodesSubtype: any));

Try Flow demo

Additional resources

这篇关于Flow不允许我将`Array&lt; A&gt;`传递给`Array&lt; A |。 B&gt;`(子类型的数组到超类型的数组)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆