Javascript 为什么在这个例子中,flow也不能更精确地确定函数的类型?

Javascript 为什么在这个例子中,flow也不能更精确地确定函数的类型?,javascript,flowtype,Javascript,Flowtype,我有一些代码可以处理一些功能性javascript概念,比如Eithers,正如Brian Lonsdorf在中介绍的那样 我一直在尝试键入一个表达式。以下是我的尝试: type RightApi<X> = { map: <C: *>(f: X => C) => RightApi<C>, fold: <C: *>(f: Function, g: X => C) => C }; const Right = <X

我有一些代码可以处理一些功能性javascript概念,比如Eithers,正如Brian Lonsdorf在中介绍的那样

我一直在尝试键入一个表达式。以下是我的尝试:

type RightApi<X> = {
  map: <C: *>(f: X => C) => RightApi<C>,
  fold: <C: *>(f: Function, g: X => C) => C
};

const Right = <X: *>(x: X): RightApi<X> => ({
  map: f => Right(f(x)),
  fold: (f, g) => g(x),
})

type LeftApi<X> = {
  map: (f: Function) => LeftApi<X>,
  fold: <C: *>(f: X => C, g: Function) => C,
};

const Left = <X: *>(x: X): LeftApi<X> => ({
  map: f => Left(x),
  fold: (f, g) => f(x),
});

const fromMayBeNullOrUndefined = <X: *>(x: X) =>
  ((x === null || x === undefined) ? Left(x) : Right(x));

const test = fromMayBeNullOrUndefined(3).fold(x => null, x => x);
// null | number
type RightApi={
映射:(f:X=>C)=>RightApi,
折叠:(f:Function,g:X=>C)=>C
};
const Right=(x:x):RightApi=>({
map:f=>Right(f(x)),
折叠:(f,g)=>g(x),
})
类型LeftApi={
map:(f:Function)=>LeftApi,
折叠:(f:X=>C,g:Function)=>C,
};
常量Left=(x:x):LeftApi=>({
映射:f=>左(x),
折叠:(f,g)=>f(x),
});
常量fromMayBeNullOrUndefined=(x:x)=>
((x==null | | x==undefined)?左(x):右(x));
const test=fromMayBeNullOrUndefined(3).fold(x=>null,x=>x);
//空|数

不幸的是,这并没有达到我想要的程度。在最后一行,我试着测试打字。从逻辑上讲,
test
将是一个
number
。因为从maybenull或undefined提供给
的值是
3
,即不是null或undefined,所以当调用
fold
时,应该调用右侧函数

我犯了一个错误吗?或者flow不可能更精确地推断结果吗

或者flow不可能更精确地推断结果吗

类型推断在函数边界处停止。如果一个函数的主体会影响另一个函数中的类型检查,那么它就太脆弱了。函数的实际主体被考虑的唯一时间是根据它自己的类型检查该函数的类型时


因此,是的,您可以通过查看
内部的
来了解
的结果,这取决于
x
是否为
null
/
未定义
,期望流也这样做是不合理的。如果您必须查看源代码以了解如何进行类型检查,那么它也会破坏函数的抽象。

听起来您正在寻找的是Flow现在可以提供的两件事情之一

  • %检查
    a.k.a

  • $NonMaybeType
    ,它是除
    null
    undefined
    之外提供的所有类型

  • 此外,流中的
    函数
    类型是不正确的,因此提供更具体的类型定义将有所帮助

    map()
    fold()
    的情况下,您可能需要查看,这允许您在编写时静态调用类型上的函数


    希望这有帮助

    您提到的问题可以通过为
    frombe或undefined
    函数定义不同类型的签名来解决,如下所示:

    declare function fromMayBeNullOrUndefined(x: null | void): LeftApi<null | void>;
    declare function fromMayBeNullOrUndefined<X>(x: X): RightApi<X>;
    function fromMayBeNullOrUndefined<X>(x: X) {
      if (x === null || x === undefined) {
        return Left(x);
      }
      return Right(x);
    }
    
    但是虽然这是一个有趣的练习,但它完全没有抓住
    类型的要点:

    one
    的要点是
    Left
    Right
    具有相同的API,因此在运行时基础值(或通常称为数据)可以是其中之一。值是
    Left
    还是
    Right
    是透明的。这一点很重要,因为当存在可能产生两种结果的某种外部影响时,您可以使用
    类型(或者任何类型的Monad)。例如,您希望连接到数据库-可以创建连接,也可以失败。或者在您的情况下,某些API的用户可能会提供数字或空值

    /* @flow */
    type Either<L, R> = {
      map: <T>(f: R => T) => Either<L, T>,
      fold: <T>(f: L => T, g: R => T) => T
    };
    
    const Right = <R>(x: R): Either<any, R> => ({
      map: <T>(f: R => T): Either<any, T> => Right(f(x)),
      fold: <T>(f: any => T, g: R => T): T => g(x),
    })
    
    const Left = <L>(x: L): Either<L, any> => ({
      map: <T>(f: any => T): Either<any, T> => Left(x),
      fold: <T>(f: L => T, g: any => T): T => f(x),
    });
    
    const fromMayBeNullOrUndefined = <X>(x: X): Either<null | void, X> =>
      ((x === null || x === undefined) ? Left(x) : Right(x));
    
    const test = fromMayBeNullOrUndefined(3).fold(x => null, x => x);
    (test: null | number)
    

    如下一节视频讲座所述,当发生多个操作时,此示例将变得更加有趣。

    Flow是否真的尝试在这种情况下执行动态分析?好问题。我的理解是这样的。这样的语法
    (x:x)=>x
    对调用函数的参数很敏感。但从这一点来看,我认为它所做的是考虑到所提供的论点的所有可能性。
    /* @flow */
    type Either<L, R> = {
      map: <T>(f: R => T) => Either<L, T>,
      fold: <T>(f: L => T, g: R => T) => T
    };
    
    const Right = <R>(x: R): Either<any, R> => ({
      map: <T>(f: R => T): Either<any, T> => Right(f(x)),
      fold: <T>(f: any => T, g: R => T): T => g(x),
    })
    
    const Left = <L>(x: L): Either<L, any> => ({
      map: <T>(f: any => T): Either<any, T> => Left(x),
      fold: <T>(f: L => T, g: any => T): T => f(x),
    });
    
    const fromMayBeNullOrUndefined = <X>(x: X): Either<null | void, X> =>
      ((x === null || x === undefined) ? Left(x) : Right(x));
    
    const test = fromMayBeNullOrUndefined(3).fold(x => null, x => x);
    (test: null | number)
    
    function getAccountBalance(name: string): string {
      const account: Either<Error, Account> = getAccountByCustomerName(name);
    
      return account
        .map(account => account.balance)
        .fold(
          () => "Seems like you don't have an account at our service",
          balance => `Your balance is ${balance}`,
        );
    }