如何配置Typescript在`is`和`never`存在时推断区分联合?

2eafrhcq  于 7个月前  发布在  TypeScript
关注(0)|答案(1)|浏览(59)

我在我的项目中有一个库,我停止使用(从package.json中删除),它的对等依赖之一是fp-ts,所以我必须将fp-ts添加到我的项目中。fp-ts有一个Either类型,可以检查左/右值:

export declare const isLeft: <E>(ma: Either<E, unknown>) => ma is Left<E>

字符串
如果我把这个放进if

if (E.isLeft(result)) {
    // ...
}


那么在else中,Typescript将正确地推断出我的Either有一个right值。
我的问题是,自从我将依赖项移动到我的项目(而不是间接地将其用作对等依赖项)之后,以下情况不再起作用,我得到了一个编译器错误:

const fail = (msg: string): never => {
    throw new GenericProgramError(msg);
};

if (E.isLeft(result)) {
    fail("Expected a successful result");
}
expect(result.right).toEqual(
    //        ^^^--- Property 'right' does not exist on type 'Left'
    // ...
);


这里的问题是,如果result是一个Left,那么I fail返回never(抛出),所以Typescript应该能够推断出在expect中,result只能有一个right,而不能有一个left。这在以前是有效的。我需要修改什么来修复这个问题?

x8diyxa7

x8diyxa71#

答案是你的failAssert函数应该是一个函数语句,而不是一个胖箭头函数。(感谢大卫_p的回答:https://stackoverflow.com/a/72689922/81723
因此,只需更改为function fail(...): never,它就可以工作:

function fail(msg: string): never {
    throw new GenericProgramError(msg);
}

字符串
下面是完整的工作示例:

import * as E from 'fp-ts/Either';
declare function expect(value: any);

const result = E.right<string, boolean>(true);

function fail(msg: string): never {
  throw new Error(msg);
}

if (E.isLeft(result)) {
  fail("Expected a successful result");
}

expect(result.right).toEqual();
//     ^^^ ✅ result: E.Right<boolean>

为什么会这样

我转弯抹角地找到了答案。
我试图通过将您的代码更改为Assert函数来解决这个问题。
但是我得到了一个奇怪的错误:“Assert要求调用目标中的每个名称都用显式类型注解声明。

const fail2 = (result: E.Either<any, any>): asserts result is E.Right<any> => {
    if (E.isLeft(result)) {
        throw new Error('Expected a successful result');
    }
};
    
fail2(result);
// Error: Assertions require every name in the call target to be declared with an explicit type annotation.(2775)


搜索这个错误得到了david_p's answer,这解释了你不能使用箭头函数作为Assert函数(好吧,技术上你可以,但是你需要显式定义被赋值变量的类型签名。在实践中,只使用函数语句更容易)。

相关问题