代码之家  ›  专栏  ›  技术社区  ›  EugeneZ

如何使用Flow来键入通用重新选择选择器?

  •  1
  • EugeneZ  · 技术社区  · 8 年前

    我有两种相似但不同的结构:

    type A = { foo: string, bar: string, specialA: string };
    type B = { foo: string, bar: string, specialB: string };
    

    我正在使用 reselect 要创建选择器,请使用 flow-typed here . 我所做的唯一修改是导出OutputSelector类型。

    我有三个文件,目的如下:

    generic.js

    export const someSelector = selector => createSelector(selector, ...)
    

    a.js :导出特定于的选择器 type A 这样地:

    import { someSelector } from './generic.js';
    
    const aSelector = state => state.a;
    
    export selector = someSelector(aSelector);
    

    b.js :与相同 a、 js 要不是 type B

    所以我的想法是将一个特定的选择器传递给泛型选择器,泛型选择器将选择适当的类型。在使用特定选择器时,我很难将其设置为保留类型信息。所以有些文件只关心 因此,它从中导入选择器 a、 js A 或 B . 由于很难准确地找出这一切的错误所在,我将选择器复制粘贴到 确保它们在没有泛型的情况下工作。然后我把一个选择器移到 试图缩小问题的范围。我有这个:

    type Base = { foo: string, bar: string };
    type SpecificSelector<T> = OutputSelector<State, mixed, T>;
    export const selector = <T: Base, A: $ReadOnlyArray<T>, R: { [string]: $ReadOnlyArray<T> }>(specificSelector: SpecificSelector<A>) =>
      createSelector(
        specificSelector,
        (arr: A): R => groupBy(arr, (o: T): mixed => finding.foo),
      );
    

    定义 lodash groupBy :

    groupBy<V, T>(
      array: $ReadOnlyArray<T>,
      iteratee?: ?ValueOnlyIteratee<T>,
    ): { [key: V]: $ReadOnlyArray<T> };
    

    我得到的错误是:

    Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ apps/acheron/src/selectors/util/generic.js:70:23
    
    Cannot return groupBy(...) because object type [1] is incompatible with R [2].
    
         apps/acheron/src/selectors/util/generic.js
          67│ export const selector = <T: Base, A: $ReadOnlyArray<T>, R: { [string]: $ReadOnlyArray<T> }>(specificSelector: SpecificSelector<A>) => createSelector(
          68│   specificSelector,
          69│
     [2]  70│   (arr: A): R => groupBy(arr, (o: T): mixed => o.foo),
          71│ );
          72│
    
         flow-typed/npm/lodash_v4.x.x.js
     [1] 708│     ): { [key: V]: $ReadOnlyArray<T> };
    

    我不明白他们为什么不相容。如果我删除泛型参数 R 只需返回 群比 ,我还有一个问题:

    export const selector1 = <T: Base, A: $ReadOnlyArray<T>>(specificSelector: SpecificSelector<A>) => createSelector(
      specificSelector,
    
      (arr: A): {[string]: $ReadOnlyArray<T>} => groupBy(arr, (o: T): mixed => o.foo),
    );
    
    export const selector2 = <T: Base, A: $ReadOnlyArray<T>>(specificSelector: SpecificSelector<A>) => createSelector(
      [selector1(specificSelector),
      (state: State, props: mixed): ?string => props && props.match && props.match.params && typeof props.match.params.appId === 'string' ? props.match.params.appId : null],
    
      (arr: {[string]: $ReadOnlyArray<T>}, appId: ?string): ?$ReadOnlyArray<T> =>
        arr && appId && arr[appId]
          ? arr[appId]
          : null,
    );
    

    新错误:

    Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ apps/acheron/src/selectors/util/generic.js:74:4
    
    Cannot call createSelector with array literal bound to selectors because:
     • Base [1] is incompatible with T [2] in array element of the indexer property of the return value of index 0.
     • T [1] is incompatible with Base [2] in array element of the indexer property of the return value of index 0.
    
     [1] 70│   (arr: A): {[string]: $ReadOnlyArray<T>} => groupBy(arr, (o: T): mixed => o.foo),
         71│ );
         72│
         73│ export const selector2 = <T: Base, A: $ReadOnlyArray<T>>(specificSelector: SpecificSelector<A>) => createSelector(
         74│   [selector1(specificSelector),
         75│   (state: State, props: mixed): ?string => props && props.match && props.match.params && typeof props.match.params.appId === 'string' ? props.match.params.appId : null],
         76│
     [2] 77│   (arr: {[string]: $ReadOnlyArray<T>}, appId: ?string): ?$ReadOnlyArray<T> =>
         78│     arr && appId && arr[appId]
    

    我理解这一点,因为lodash返回的对象不再是泛型参数,类型信息正在丢失,Flow无法确保两个对象中包含相同的键?

    0 回复  |  直到 8 年前