I wanted to discuss this in a dedicated discussion because the details will get
involved:
> Hmm. I’m not sure it’s working as I’d expect.
>
> In Core, there’s TaskTest which has this:
>
>
> [Test(async,timeout="50")]
> public function testPromiseToTask():void
> {
> var resolvePromise:Promise = new
> Promise(function(resolve:Function, reject:Function):void{
> resolve("resolved");
> });
> var resolvedTask:PromiseTask =
> promiseToTask(resolvePromise);
>
> resolvedTask.done(function(task:PromiseTask):void{
> assertEquals(task.result, "resolved");
> });
> resolvedTask.run();
> }
>
> The compiler is now complaining:
>
> Implicit coercion of a value of type
> (task:org.apache.royale.utils.async.PromiseTask)=>void to an unrelated type
> (task:org.apache.royale.utils.async.IAsyncTask)=>void.(1067)
>
> A PromiseTask extends AsyncTask which implements IAsyncTask.
>
> I think that should be allowed.
I think I understand why it’s working like that, but we want a feature which is
more useful than annoying.
A function has two parts: 1. The arguments 2. the return value.
When passing a function into a client, the client is a consumer of the return
value, but a “producer” of the arguments. Since the function is expecting a
more specific type than the “client” is producing, that might cause a type
mismatch. My assumption is that this error is specific to parameters and not
return types.
Now, while this behavior makes sense from a type-safety perspective, I’m not
sure how useful it will be practically. The typescript type system often tends
to be annoying enough that people often just revert to using “any”. I’d like to
find the right balance of type safety and usefulness. With that in mind, I’d
like to lay out where I see this feature being used and useful and figure out
if we have those bases covered. I’m going to discuss specific cases because
that’s easier for me than discussion the abstract.
1. AsyncTasks take callbacks which are called when the task is done. The
callbacks are always called by passing in the task in the task instance. The
methods (done and exec) are defined in the base class and we don’t want to
redefine those for every subclass. The callback is handled differently for each
task, so the specific type is important in the callback definition.
2. Event callbacks. All EventDispatchers dispatch events which are of type
Event. Specific classes will dispatch more specific events. It’s useful to make
sure that event listeners are the right shape. It would be even more useful to
know which events should be handled for specific event listeners.
3. Runtime swappable utility functions which calculate some value. (One example
of this is labelFunction.) The return type of these functions should be well
defined. The arguments should be pretty well defined as well.
The first case is not working very well with the current implementation. By
definition, the type supplied to the callback will always be the type of the
specific instance of AsyncTask. Ideally we should have a “this” type we can
declare which would be inferred from the current instance.
The second case is a more difficult problem. The specific event type dispatched
cannot be known because a single class can dispatch different event types
depending on the specific event. I’m not sure how it’s possible to even
decorate all the options. Maybe if we had union types we could do something.
For this case it feels like we need some way to declare a type in the function
parameter that’s explicitly telling the compiler “I know this type is more
specific than what’s expected, but that’s OK”. I do think that it should have
an error if it doesn’t match the less specific definition though.
For the third case, I think the implementation as it is, is fine.
Thoughts?
Harbs