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

Empty object or type ({} | Type) doesn't work as expected #8032

Closed
alexgorbatchev opened this issue Apr 12, 2016 · 8 comments
Closed

Empty object or type ({} | Type) doesn't work as expected #8032

alexgorbatchev opened this issue Apr 12, 2016 · 8 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@alexgorbatchev
Copy link

TypeScript Version:

1.8.9

Code

interface IAnimal {
  legs: Number;
}

interface IHouse {
  cat: {} | IAnimal;
}

const obj: IHouse = {
  cat: {
    legs: 2,
  },
};

obj.cat.legs = 5;   // Property 'legs' does not exist on type '{} | IAnimal'.
obj.cat = {};

Expected behavior:

Should be able to assign cat.legs or {} interchangeably.

Actual behavior:

Unable to assign legs of {} | IAnimal.

@RyanCavanaugh
Copy link
Member

You can't assign the legs property to {} (it has no members), and the cat property might be {}.

What was your intent in writing {} | IAnimal ? This is a somewhat unusual thing to write and it's probably not doing what you think it does.

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label Apr 12, 2016
@alexgorbatchev
Copy link
Author

You are most likely correct. I'm trying to have a property where it could be a predefined type or an empty object {}.

{} | IAnimal seemed like an intuitive thing to do, I couldn't find an existing example.

@RyanCavanaugh
Copy link
Member

I'm trying to have a property where it could be a predefined type or an empty object {}.

In that case, what you wrote is correct (usually people use null or undefined as a placeholder, hence my question).

For this example, the best solution would be a type assertion, since the compiler doesn't know whether or not the cat property is {} or not:

interface IAnimal {
  legs: Number;
}

interface IHouse {
  cat: {} | IAnimal;
}

const obj: IHouse = {
  cat: {
    legs: 2,
  },
};

(obj.cat as IAnimal).legs = 5;   // OK
obj.cat = {};

There are other solutions you could play with (writing a type predicate, making legs optional, etc) but it really depends on the situation.

As an aside, you definitely want to use number instead of Number. Number refers to the boxed object you get from e.g. new Number(5); number refers to actual numbers you'd commonly see in JS.

@alexgorbatchev
Copy link
Author

Interesting, thanks for clearing that up. I believe I see how it works now. Because there's no clear way to discern between {} and IAnimal compiler just choses the first one from {} | IAnimal, is that right?

I got it work with making legs optional, which isn't ideal, because it's either all properties or an empty object (the actual code is more involved).

Thanks for the number correction, I haven't realized that.

@RyanCavanaugh
Copy link
Member

Because there's no clear way to discern between {} and IAnimal compiler just choses the first one from {} | IAnimal, is that right?

Basically. The rule for union types is that we only allow an operation if it would be legal to do on each member of the union. Since {} doesn't have legs, the property assignment isn't allowed.

Depending on context we can sometimes tell which you happen to have and will "narrow" to one member of the union (e.g. if there's an instanceof or typeof check), but this situation isn't one of them.

@tonyxiao
Copy link

tonyxiao commented Mar 3, 2018

@RyanCavanaugh what if I actually want to define an empty object that may not have any properties? (such that the only acceptable value for that object is {})? How would I define the type for that in TypeScript?

@mhegazy
Copy link
Contributor

mhegazy commented Mar 3, 2018

use object instead.

@evenfrost
Copy link

object throws same error. I'm using this to define Vuex default state structure. Any way to define empty object | Type without making all properties of Type optional?

@microsoft microsoft locked and limited conversation to collaborators Jul 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

5 participants