1

我正在尝试使用 TypeScript 并尝试使用fp-ts类型对域逻辑进行建模,但我遇到了这个问题:

import { left, right, Either } from "fp-ts/lib/Either";

type MyType = {
  id: string,
  isValid: boolean,
}

type MyValidType = {
  id: string,
  isValid: true,
}

type CreateMyValidType = (t: MyType) => Either<Error, MyValidType>

// Compile error!
const createMyValidType: CreateMyValidType = t => {
  switch (t.isValid) {
    case true:
      return right({
        id: "test",
        isValid: true
      })
    default:
      return left(new Error())
  }
}

编译器对我大喊大叫,因为: Type '(t: MyType) => Either<Error, { id: string; isValid: boolean; }>' is not assignable to type 'Either<Error, CreateMyValidType>'.

如果我删除Either并且我只返回 sum 类型Error | MyValidType就可以了。

type CreateMyValidType = (t: MyType) => Error | MyValidType

// This compiles
const createMyValidType: CreateMyValidType = t => {
  switch (t.isValid) {
    case true:
      return {
        id: "test",
        isValid: true
      }
    default:
      return new Error()
  }
}

它在里面时似乎无法识别正确的类型Either

我找到了通过指定right调用时的类型来避免问题的方法,但我不完全理解其中的含义,所以我不知道这是否是一个坏主意:

return right<Error, MyType2>({
  id: "test",
  isValid: true,
});

处理此问题并使其编译的正确方法是什么?谢谢!

4

1 回答 1

2

简短的回答

它与 TS >= 一起按预期工作3.4

稍微长一点的回答

您可能已经注意到,TypeScript 通常并不擅长推理。在您的代码示例中,您为 function 的返回类型提供了注解Either<Error, MyValidType>,以便 TS 可以尝试将所有分支统一为预期的返回类型:如果没有此显式注解,结果会更糟。

即使使用手动类型注释,3.4 之前的 TS 也会“懒惰”并尝试解决所有由leftandright函数声明的泛型类型参数(具有LR作为类型参数),而无需“等待”以获得更好的知识在做出那个选择之前。因此,它会推断出Error案件和案件。问题是它需要是文字(比 更具体),因此它最终以失败告终Ldefault{ id: string, isValid: boolean }RtrueMyValidTypeisValidtrueboolean

Type '{ id: string; isValid: boolean; }' is not assignable to type 'MyValidType'.
  Types of property 'isValid' are incompatible.
    Type 'boolean' is not assignable to type 'true'.

在 TS >=3.4R情况下,直到过程后期,当 TS 实际上知道预期的(注释的)返回类型createMyValidType,并且正确地将文字对象视为可分配给声明的返回类型时,它才会“未决定”。

您可以在https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#higher-order-type-in ​​ference-from-generic-functions 的官方变更日志中阅读有关此改进的更多信息

注1

这个问题与 没有真正的关系fp-ts,因为任何通用函数都会在 3.4 之前发生类似的问题:

declare function identity<T>(t: T): T;

function f(): { foo: 'foo' } {
  return identity({ foo: 'foo' });
}
// Type '{ foo: string; }' is not assignable to type '{ foo: "foo"; }'.
//   Types of property 'foo' are incompatible.
//     Type 'string' is not assignable to type '"foo"'.

笔记2

查看此示例的另一种方式是,TS 默认情况下不会推断出最精确的可能文字类型,除了某些特定情况:

const foo = 'foo' // Type: "foo"

const fooObj = { foo: 'foo' } // Type: { foo: string }

考虑到 JS 的可变性,这是一个“安全”的默认值。可以使用“const断言”更改此行为:

const fooObj = { foo: 'foo' } as const // Type: { readonly foo: "foo" }

这是3.4(请参阅https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#const-assertions)中的另一个添加,由于返回类型注释,因此在您的示例中并不严格需要你有createMyValidType

于 2019-04-23T17:38:59.407 回答