mobx 自定义类型

使用 ts + mobx 开发的时候,有时候会遇到我们定义了可枚举的联合类型的情况,但是 mobx 自带的 types 默认不支持可枚举的联合类型,怎么办呢?

类型定义

比如此时我们有可枚举的联合类型如下

1
type TKind = 0 | 1 | 2

如果在 mobx 定义中使用 types.number 肯定是不对的,类型提示对不上

这时我们可以使用 types 提供的 types.custom 方法来构造自定义的 types

types.custom

跳转到方法原型,我们可以看到该方法要求我们传入一组泛型和一个 option

该泛型要求传入两个类型,表示这个 types 执行的是从类型 T 到类型 P 的转换

该 option 包含 5 个必填字段

  1. name : string,表示这个新 types 的 name,似乎可以重复,但最好还是不要重复
  2. fromSnapshot,表示如何将类型 T 的值转换为类型 P 的值
  3. toSnapshot,表示如何将类型 P 的值转换为类型 T 的值
  4. isTargetType,表示传入的类型 T 的值能否转换为类型 P 的值
  5. getValidationMessage,表示 isTargetType 返回 false 时,报错的信息是什么

但我们只想定义一个类型,这时怎么平衡 T 和 P 的关系呢?

我们可以看看 types 的实际返回值

types 的返回值

举例,比如 types.numbertypes.string 这些形如 types.<T> 的 types,它们都返回 ISimpleType<T>

ISimpleType<T> 又 extends IType<T, T, T>,所以最终返回的是 IType<T, T, T>

我们只需要构造出能返回 IType<T, T, T> 的 types 即可

types.custom 的原型如下

1
declare function custom<S, T>(options: CustomTypeOptions<S, T>): IType<S | T, S, T>;

可见当我们向 types.custom 填入的泛型为 <T, T> 时,该方法就会返回 IType<T, T, T>,就可以达到我们的目标

获得自定义 types

有了如上类型推断,我们容易得到这么一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const customType = <T>({
name,
acceptedValue,
}: {
name: string
acceptedValue: T[]
}) => {
return types.custom<T, T>({
name,
fromSnapshot: value => value,
toSnapshot: value => value,
isTargetType: value => {
return acceptedValue.includes(value)
},
getValidationMessage: value => {
if (acceptedValue.includes(value)) {
return ""
} else {
return `${value} is not a value in ${acceptedValue.toString()}`
}
},
})
}

对于上述 option 中的 5 个字段,我们可以做如下处理

  1. name 要求调用方传入
  2. fromSnapshot、toSnapshot 因为类型一致,所以均可以直接返回自身
  3. 因为我们的目标是可枚举的联合类型,而可枚举的联合类型是可以算出符合条件的值域的,所以要求调用方传入 acceptedValue 数组以辅助判断
  4. 报错信息就比较随意了,随便写写即可。此处因为我们要求 acceptedValue 必须是个数组,所以必然有 toString 方法可以调用

定义自定义 types

有了方便的获取新 types 的函数,我们就可以动手了

对于本文开头的定义,我们可以写为如下形式

1
2
3
4
5
6
7
8
9
type TKind = 0 | 1 | 2
const myTypes: {
kind: ISimpleType<TKind>
} = {
kind: customType<TKind>({
name: "myKind",
acceptedValue: [0, 1, 2]
})
}

之后就可以像默认的 types 一样使用了,示例如下

1
2
3
4
5
const model = types.model("store").props({
id: types.string,
count: types.number,
kind: myTypes.kind
})

都会有形如 ISimpleType<T> 的 ts 提示


感谢阅读

--It's the end.Thanks for your read.--