代码之家  ›  专栏  ›  技术社区  ›  Rory O'Kane Erce

Flow不允许我将“Array<A>”传递给“Array<A | B>”(子类型数组传递给父类型数组)

  •  1
  • Rory O'Kane Erce  · 技术社区  · 6 年前

    我有一个类型的值 Array<A> Array<A | B> (超级类型的数组),尽管它显然有效。

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

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

    引发此错误:

    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

    Array<ANode> 到一个需要 Array<Node> Node 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

    1 回复  |  直到 6 年前
        1
  •  4
  •   Rory O'Kane Erce    6 年前

    Flow引发错误,因为它认为 可能会改变数组

    // 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

    在运行上述代码之后, nodesSubtype 会包含一个 BNode 即使它被声明为 ANode ,违反了它的类型。

    有两种方法可以让Flow相信你不会改变数组。最清楚的是 代替 Array 具有 $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

    您只需在函数参数类型中进行替换(例如。 nodes ),但如果你喜欢,你可以使用 $只读阵列 它适用于任何地方(例如 ).

    是最安全的类型解决方案,但您可能不想更改所有现有函数来使用它。在这种情况下,你的选择是 将数组类型转换为 any 为什么?

    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

    如果你不关心记录问题,你可以省略注释,只将其转换为 任何

    getFirstNodeType((nodesSubtype: any));
    

    Try Flow demo

    额外资源