使用 TypeScript 和 fp-ts 进行类型建模以及使用 Either 时出现错误
Type modeling with TypeScript and fp-ts and error while using Either
我正在尝试使用 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
并且我只是 return 总和类型 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,
});
处理这个问题并使其编译的正确方法是什么?
谢谢!
简答
在 TS >= 3.4
下它按预期工作
稍微长一点的回答
您可能已经注意到,TypeScript 通常在推理方面并不是很好。
在您的代码示例中,您为函数 Either<Error, MyValidType>
的 return 类型提供了注释,以便 TS 可以尝试将所有分支统一为预期的 return 类型:没有此显式注释,结果会更糟。
即使使用手动类型注释,3.4 之前的 TS 也会是 "lazy" 并尝试解析由 left
和 right
函数声明的所有泛型类型参数(两者都有L
和 R
作为类型参数)就位,没有 "waiting" 在做出选择之前获得更好的知识。
因此,对于 default
的情况,它将 Error
推断为 L
,对于 true
的情况,将 { id: string, isValid: boolean }
推断为 R
。问题是 MyValidType
要求 isValid
是文字 true
(比 boolean
更具体),因此它最终以
失败
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.4
,R
被保留 "undecided" 直到过程的后期,当 TS 实际上知道 return 的预期(注释)类型时=32=],并正确地将文字对象视为可分配给声明的 return 类型。
的官方更新日志中阅读有关此改进的更多信息
注一
这个问题与 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 }
这是 "safe" 考虑到 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
中的 return 类型注释,您的示例中并不严格需要它.
我正在尝试使用 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
并且我只是 return 总和类型 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,
});
处理这个问题并使其编译的正确方法是什么? 谢谢!
简答
在 TS >= 3.4
稍微长一点的回答
您可能已经注意到,TypeScript 通常在推理方面并不是很好。
在您的代码示例中,您为函数 Either<Error, MyValidType>
的 return 类型提供了注释,以便 TS 可以尝试将所有分支统一为预期的 return 类型:没有此显式注释,结果会更糟。
即使使用手动类型注释,3.4 之前的 TS 也会是 "lazy" 并尝试解析由 left
和 right
函数声明的所有泛型类型参数(两者都有L
和 R
作为类型参数)就位,没有 "waiting" 在做出选择之前获得更好的知识。
因此,对于 default
的情况,它将 Error
推断为 L
,对于 true
的情况,将 { id: string, isValid: boolean }
推断为 R
。问题是 MyValidType
要求 isValid
是文字 true
(比 boolean
更具体),因此它最终以
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.4
,R
被保留 "undecided" 直到过程的后期,当 TS 实际上知道 return 的预期(注释)类型时=32=],并正确地将文字对象视为可分配给声明的 return 类型。
注一
这个问题与 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 }
这是 "safe" 考虑到 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
中的 return 类型注释,您的示例中并不严格需要它.