'How to type annotate MobX observable objects using non-OOP approach?
Please, consider this example:
import { observable } from "mobx";
interface MyObject {
name: string;
age: number;
}
const obj1 = { namee: "Uldis", age: "35" };
type MyObjectObservable = ???;
const obj1Observable: MyObjectObservable = observable(obj1); // I want to have compiler error here
function doStuff(obj: MyObjectObservable) {}
doStuff({ name: "Uldis", age: 35 }) // I want to have compiler error here because passed object is not an observable
How should type for MyObjectObservable be defined? I want to have as strict types as possible and I don't want to use OOP approach because of other type related issues such as not having precise subtypes for the state that methods use.
Solution 1:[1]
There should be no error in Typescript when using observable or makeObservable
This is all valid code:
import { makeAutoObservable, observable } from "mobx"
interface MyObject {
name: string
age: number
}
const obj1 = { name: "Uldis", age: 35 }
const make: MyObject = makeAutoObservable(obj1)
const obj1Observable: MyObject = observable(obj1)
function doStuff(obj: MyObject) {}
doStuff({ name: "Uldis", age: 35 })
doStuff(make)
doStuff(obj1Observable)
Solution 2:[2]
I see two possible solutions:
- Use boxed observable
import { observable, IObservableValue } from "mobx";
interface MyObject {
name: string;
age: number;
}
const obj1 = { namee: "Uldis", age: "35" };
type MyObjectObservable = IObservableValue<MyObject>;
const obj1Observable: MyObjectObservable = observable.box(obj1);
obj1Observable.get(); // returns MyObject
- Add your own branded
Observabletype in order to avoid wrapping with an additional object.
//customObservable.ts
import {observable as observableImpl} from 'mobx'
const mobx = Symbol('mobx');
export type Observable<T> = T & { [K in typeof mobx]: never };
export const observable = <T extends object>(obj: T): Observable<T> => {
return observableImpl(obj) as Observable<T>;
};
import { observable, Observable } from "./customObservable";
import { observable as originalObservable} from "mobx";
interface MyObject {
name: string;
age: number;
}
const obj1 = { namee: "Uldis", age: "35" };
type MyObjectObservable = Observable<MyObject>;
const obj1Observable: MyObjectObservable = observable(obj1);
const obj2Observable: MyObjectObservable = originalObservable(obj1); // compile-time error
obj1Observable.name; //string
obj1Observable.age; //number
Solution 3:[3]
After looking at this more closely I've came up with this solution:
import { makeObservable,IObservableValue } from "mobx"
type MyObservable=IObservableValue<{name:string}>
const obj = makeMyObservable({ name:'Johny' })
const obj2 = {name:'Johny'}
function makeMyObservable<T extends Record<string,any>>(val:T){
return makeObservable(val) as unknown as IObservableValue<T>
}
function onlyMyObservable(val:MyObservable){
return val
}
onlyMyObservable(obj)
onlyMyObservable(obj2) // error
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Ivan V. |
| Solution 2 | robak86 |
| Solution 3 | Ivan V. |
