- Notifications
You must be signed in to change notification settings - Fork43
Description
Let's say that Cat extends Animal – all Cats are Animals, just with even more restrictions on top. A Dispatchable<Animal> should be assignable to Dispatchable<Cat>. Only Cats will be dispatched to Dispatchable<Cat>, and all Cats are Animals, so a Dispatchable<Animal> is suited to process all Cats that is dispatched to it. Cat is a variant of Animal, but Dispatchable<Animal> is a variant of Dispatchable<Cat>, not the other way around. This means that the relationship between Dispatchable and its type parameter Msg is contravariant.
typeLeastRestrictedType={field1:"field1";};typeMiddleRestrictedType=LeastRestrictedType&{field2:"field2"};typeMostRestrictedType=MiddleRestrictedType&{field3:"field3"};constsystem=start();constactor=spawn(system,(state:undefined,message:MiddleRestrictedType):undefined=>{console.log(message.field1,message.field2);returnstate;});// This should fail because the actor actually uses restrictions// for MiddleRestrictedType that are not present on LeastRestrictedType.constsmallerActorRef:Dispatchable<LeastRestrictedType>=actor;dispatch(smallerActorRef,{field1:"field1",});// This fails, but shouldn't because all restrictions on MiddleRestrictedType// are also present on MostRestrictedType.constbiggerActorRef:Dispatchable<MostRestrictedType>=actor;dispatch(biggerActorRef,{field1:"field1",field2:"field2",field3:"field3",});
Expected Behavior
- The TypeScript type system should realize that Dispatchable and its type parameter Msg are contravariant.
- Trying to assign Dispatchable<Cat> to Dispatchable<Animal> should fail.
- Trying to assign Dispatchable<Animal> to Dispatchable<Cat> should succeed.
Current Behavior
- Trying to assign Dispatchable<Cat> to Dispatchable<Animal> succeeds, which leads to the bug illustrated above where the type system allows a message to be passed to a Dispatchable that doesn't have all the restrictions it assumes/needs.
- Trying to assign Dispatchable<Animal> to Dispatchable<Cat> fails, even though all Dispatchable<Animal> are perfectly able to process all Cats.
Possible Solution
Probably some in/out annotations on type parameters in Dispatchable and associated types, to caress the type system into realizing the relationship between Dispatchable and its type parameter Msg is contravariant.
Context
I have met this roadblock many times. Most recently, I tried to create a testing function that expected a LocalActorRef<A | B>, to which I tried to pass a LocalActorRef<A | B | C>. If it can process A, B, and C, it will have no trouble if it only gets A and B, but the type system doesn't like this.