You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
The text was updated successfully, but these errors were encountered:
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.
Expected Behavior
Current Behavior
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.
The text was updated successfully, but these errors were encountered: