you can't implement {}
This is simply not true. This is allowed:
type Empty = {};
class B implements Empty {
}
Granted, it does not make much sense - implements Empty
does not add any information for the users of B
, and does not add any constraint on the implementation.
Why type {} even exist?
If you have operations on types named "intersection" and "union", you will sooner or later want to have a type X
that, for any type T
, satisfies the equivalences T | X = T
and T & X = X
. This X
is {}
, a type which is defined not to have any properties. Why does it exist? For the same reason an empty set exists. Where it can be useful? Anywhere when you need to have a concrete type, but don't know exactly which properties it could have. Here is one example:
type BaseEventHandlers<Event extends string> = { [event in Event]: {} };
// in BaseEventHandlers we don't know exact type of data that comes with different event types,
// so we have it as {}
// it's better than any because any can have unpredictable effect on type checking
// it's better than Object because Object has properties with names
// that could possibly interfere with our data
type EventHandlers<H extends BaseEventHandlers<string>> = { [event in keyof H]: (args: H[event]) => void };
// here we can express a constraint that each event handler
// must receive an object of appropriate type
// example
type UserEvents = {
user_added: { name: string };
user_joined_team: { userName: string, teamName: string };
}
const userHandlers: EventHandlers<UserEvents> = {
// what is checked by the compiler here:
// all events have handlers assigned
// each handler has correct names for arguments
// argument types are inferred from types in `UserEvents`
// and do not need to be repeated here
user_added: ({ name }) => {
},
user_joined_team: ({ userName, teamName }) => {
}
}
Update
Oops, it turns out that {}
, as well as any primitive type like string
, is still implicitly considered to have all predefined properties of an Object
- as noted by J.Doe, this gives no error:
const userHandlers: EventHandlers<UserEvents> = {
// what is checked by the compiler here:
// all events have handlers assigned
// each handler has correct names for arguments
// argument types are inferred from types in `UserEvents`
// and do not need to be repeated here
user_added: ({ name, toString }) => {
So my answer is little more than wishful thinking - that's what I want {}
to be, but in reality there is very little difference between {}
and Object
. They are still different:
type o = keyof Object; // 'constructor' | 'toString' | ...
type e = keyof {}; // never
but the difference is subtle and I'm not sure how it can be used in practice. Personally, I prefer to use {}
to represent type without any properties, and Object
when I need to refer to Object
prototype provided by Javascript runtime. So the difference is mostly notional, pretty much the same way as the difference between string
and String
.