我正在尝试使用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
而只返回了总和类型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,
});
处理此问题并将其编译的正确方法是什么? 谢谢!
答案 0 :(得分:2)
简短答案
在TS> = 3.4
答案略长
您可能已经注意到,TypeScript通常在推理方面并不出色。
在您的代码示例中,为函数Either<Error, MyValidType>
的返回类型提供注释,以便TS可以尝试将所有分支统一为期望的返回类型:如果没有此显式注释,结果将更糟。 / p>
即使使用手动类型注释,3.4之前的TS也将是“懒惰的”,并尝试解析left
和right
函数声明的所有通用类型参数(都具有{{1} }和L
作为类型参数),而无需“等待”以在选择之前获得更好的知识。
因此,对于R
情况,它将Error
推断为L
,对于default
情况,它将推断{ id: string, isValid: boolean }
为R
。问题是true
要求MyValidType
是原义的isValid
(比true
更具体),因此最终失败
boolean
在TS> = 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'.
的情况下,3.4
会一直处于“不确定状态”,直到TS真正知道预期的{带注释的)返回类型R
并正确看到可分配给声明的返回类型的文字对象。
注释1
该问题与createMyValidType
并没有真正的关系,因为对于任何泛型函数,在3.4之前都发生了类似的问题:
fp-ts
注释2
查看此示例的另一种方法是,默认情况下,TS不会推断出最精确的可能的文字类型,除了某些特殊情况:
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"'.
考虑到JS的可变性,这是“安全”的默认设置。可以使用“ const foo = 'foo' // Type: "foo"
const fooObj = { foo: 'foo' } // Type: { foo: string }
断言”来更改此行为:
const
这是const fooObj = { foo: 'foo' } as const // Type: { readonly foo: "foo" }
中的另一项附加内容(请参见https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#const-assertions),由于您在3.4
中具有返回类型注释,因此在示例中并非必须如此。