Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(api): tell consumers when their objects are observable #484

Merged
merged 1 commit into from Sep 23, 2016

Conversation

spiffytech
Copy link

See issue #483.

@coveralls
Copy link

Coverage Status

Changes Unknown when pulling 081d723 on spiffytech:master into * on mobxjs:master*.

@mweststrate
Copy link
Member

Thanks for the PR, will consider this for the next release

@mweststrate
Copy link
Member

This typing is actually not correct, an object being converted to an observable is not an IObservable itself, it has an IObservable backing object instead, as defined here:

export interface IIsObservableObject {
. I don't feel that exposing that interface however adds value, it is just the internal administration.

What could be done is introducing an empty IObservableObject interface. That has in itself not that much value, except that it could be used as typeguard for isObservableObject, so that people can express that they want to have some observable object as arguments to their functions. see also: #487

@spiffytech
Copy link
Author

spiffytech commented Aug 23, 2016

Having observable({}) return T & IObservableObject, with IObservableObject being an empty interface would satisfy my request.

Just to be clear, I'm not looking for TypeScript's type guards - they're designed to enhance type safety of code branches used in runtime type checks. I'm looking to improve compile-time checks, like so:

@action
function updateMyFoo(foo: MyInterface & IObservableObject) {
  foo.some_prop = true;  // triggers observers
}

const foo: MyInterface = {some_prop: false};
updateMyFoo(foo);  // compiler error - not observable

const observable_foo = observable(foo);  // MyInterfyace & IObservableObject
updateMyFoo(observable_foo);  // success

Would you like me to update my pull request to back out the use of IObservable, and create + use the empty IObservableObject interface?

I could add the type guard to isObservableObject a la #487 while I'm at it.

@mweststrate
Copy link
Member

Yep, that makes sense to me :)

@Strate
Copy link
Contributor

Strate commented Aug 23, 2016

@spiffytech unfirtunatelly, due to structural typing system limitation (which TYpeScript is based on), you will not receive compile time errors here, because TS will not check interfaces on object, it will check structure of object. It means, that having empty interface is almost useless.
There is a suggestion to improve TS in this part, but still needs proposal: microsoft/TypeScript#202

@mweststrate
Copy link
Member

dôh indeed

@Strate
Copy link
Contributor

Strate commented Aug 23, 2016

There is a cool trick, I've found in comments: microsoft/TypeScript#202 (comment)
Maybe we could use it here :)

@spiffytech
Copy link
Author

@Strate Can you clarify how your suggestion works with empty interfaces? The below code sample compiles for me, when it should produce an error - updateMyFoo() is still allowing an argument of type MyInterface.

interface IObservableObject {}

interface MyInterface {
    some_prop: boolean;
}

function updateMyFoo(foo: MyInterface & IObservableObject) {
    foo.some_prop = true;
}

const foo: MyInterface = {some_prop: false};
const observable_foo = <MyInterface & IObservableObject> foo;

updateMyFoo(foo);  // compiles :(

updateMyFoo(observable_foo);  // compiles

@spiffytech
Copy link
Author

@mweststrate May I assume from your previous comment that you wouldn't be comfortable exposing the IIsObservableObject interface (on an identical interface named IObservableObject), even if it's the only way to satisfy this feature request?

@mweststrate
Copy link
Member

@spiffytech indeed, the mobx$ is an internal concept and should be hidden to the outside world as much as possible.

something like interface IObservableObject { "observable-object-marker": IObservableObject } is the trick I think. That attribute doesn't have to be exists at runtime, it is just that the compiler needs to think that it is returned from the observable function :)

@spiffytech
Copy link
Author

Ah, I see! Confirmed, that makes the appropriate error occur in my code sample.

I'll update my full request with this.

@mweststrate
Copy link
Member

@spiffytech did you by any chance proceed on this one ?

@spiffytech
Copy link
Author

Yeah, I adopted what we discussed, added the type guard to isObservable, and added some unit tests for both.

I was hoping to find a way to unit test the failure case for the type guard, but everything I could think of prevented the TypeScript tests from compiling.

@mweststrate
Copy link
Member

Just merged, thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants