sylvainpolletvillard / ObjectModel

Strong Dynamically Typed Object Modeling for JavaScript
http://objectmodel.js.org
MIT License
467 stars 30 forks source link

Promises? #88

Closed ryan-mahoney closed 6 years ago

ryan-mahoney commented 6 years ago

What if a function returns a promise? Is that something that this library handles? Sorry if this is something very easy to do... just didn't see it in the documentation.

Thanks!

sylvainpolletvillard commented 6 years ago

Promise is a type among others. What do you expect from the library ? A code example could help you get your point accross

ryan-mahoney commented 6 years ago

Thank you for response -- I was doing it wrong!

I had:

import { FunctionModel, Model } from "objectmodel";

const inputType = Model({ name: String });
const returnType = Model({ count: Number });
const fn = async ({ name }) => ({
  count: await new Promise(resolve =>
    setTimeout(() => {
      resolve(name.length);
    }, 100)
  )
});

const typedFn = FunctionModel(inputType).return(returnType)(fn);

console.log(typedFn({ name: "Ryan" }));

But it should have been:

import { FunctionModel, Model } from "objectmodel";

const inputType = Model({ name: String });
const returnType = Promise;
const fn = async ({ name }) => ({
  count: await new Promise(resolve =>
    setTimeout(() => {
      resolve(name.length);
    }, 100)
  )
});

const typedFn = FunctionModel(inputType).return(returnType)(fn);

console.log(typedFn({ name: "Ryan" }));

I guess there is probably no way to add type checking to what a promise will eventually resolve to... which I think is fine, because that data would be checked (in my case) when it is passed as input to another function.

Sorry for false alarm!

sylvainpolletvillard commented 6 years ago

Actually it is possible to typecheck what the promise will eventually resolve to, but this has to be done once the promise is resolved. Because validation is done at runtime, it is impossible to validate before the promise actually resolved:

let PromiseModel = BasicModel(Promise);
let NumberModel = BasicModel(Number);

x = new Promise(resolve => setTimeout(() => resolve(42), 1000));
PromiseModel .test(x) // true
NumberModel.test(x) // false

x.then(resolvedValue => {
  NumberModel.test(resolvedValue) // true
})

You won't be able to validate both the promise and the promise resolved value at the same place, like you would do with a static typechecker like TypeScript and Promise<Number>. A static typechecker analyze all your code at the sametime, it doesn't care what is async and what is synchronous. But what if you need to validate something dynamically, like an async network response of your API ? ObjectModel can validate this kind of stuff, but the validation has to be done once the promise resolved:

const PromiseOf = model => p => Model(Promise)(p).then(x => model(x));

const asyncPromiseModel = PromiseOf(Model({ count: Number }))
x = new Promise(resolve => setTimeout(() => resolve({ count: 42 }), 1000));

asyncPromiseModel(x).then(({ count }) => {
   // ObjectModel has validated both the Promise type, and the Promise resolved value model
  console.log(count);
})
ryan-mahoney commented 6 years ago

Very cool!

Maybe this is something that you might consider adding to the documentation. I had done a cursory search of the documentation / examples for the word "promise", and since I didn't see it I made a false assumption about the capabilities.

sylvainpolletvillard commented 6 years ago

I suppose I can add an entry in the FAQ section. The thing is that library doesn't have to do something special to handle promises, you just wrap what you want to validate in a model and that's it. Same goes for Observables or other async structures.

ryan-mahoney commented 6 years ago

Now that we have had this conversation I completely understand your point. I do think though, for a new user of the library there may be an expectation to see how this library handles Promises because they are a little bit of a special case in Javascript... at least when you are using the async / await syntax.

sylvainpolletvillard commented 6 years ago

I added an entry to the FAQ in the docs