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

Cannot cast union type to an implemented interface if the class contains more properties #6960

Closed
steinso opened this issue Feb 7, 2016 · 3 comments
Labels
Duplicate An existing issue was already created

Comments

@steinso
Copy link

steinso commented Feb 7, 2016

interface IFoo {
    foo: string;
}

interface IBar {
    bar: string;
}

class Foo implements IFoo{
    foo: string;
}

class Bar implements IBar{
    bar: string;
    newProperty: string;
}

let x:Foo|Bar = new Bar();
let y:IFoo =  x as IFoo; // Works as expected, no errors

let u:Foo|Bar = new Bar();
let z:IBar = u as IBar; //Does not work IFF Bar contains more properties than IBar

We have a situation where a property is a union of several classes, and we would like to cast it to the correct interface in different situations. However this does not seem to work if the class has properties that is not present on the implemented interface. Playground link

@xLama
Copy link

xLama commented Feb 7, 2016

I think it´s ok.

Consider this example with objecs literals. I think it is what you are trying to do.

var ibar : { bar:string, newProperty:string} = { foo: "foo"}  // Error. It´s nos assignable.

Or with your code:

let z:IBar = u as IBar; //This line is similar to next one
var ibar: IBar = new Foo(); // Error. 

``

@steinso
Copy link
Author

steinso commented Feb 7, 2016

Thanks for your input! However I think I have not made my intentions clear enough, so I will clarify.

When you make a class that implements an interface, you are allowed to add more properties without error, as long as you uphold your contract with the interface.

What I am trying to do is this (just with an added union)

interface IBar {
    bar: string;
}

class Bar implements IBar{
    bar: string;
    newProperty: string;
}

let u: Bar = new Bar();
let z: IBar = u as IBar; 

This does not produce an error in Typescript, and I assume this is because the class Bar implements the interface IBar, therefore it is guaranteed that casting an instance of bar to IBar should not break the contract of the IBar interface.

This allows us to restrict our knowledge of an instance to only the information that we need in the current context.

I want to use this principle with unions. So instead of knowing that the instance is a Bar class, I know that it is either a Bar instance or a Foo instance. I do not want to cast directly to Foo or Bar, because I only need to know the interfaces they implement, so instead I want to cast to IFoo or IBar, because I know for a fact which it is during execution.

@ahejlsberg
Copy link
Member

This is the same issue as #5300. We have a fix in #5517 that we're still discussing.

@ahejlsberg ahejlsberg added the Duplicate An existing issue was already created label Feb 8, 2016
@RyanCavanaugh RyanCavanaugh reopened this Feb 8, 2016
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants