Add TypeScript To An Existing React Project
Learn how to add TypeScript to your existing React project in a few simple steps.
The empty object type - {}
- doesn't behave how you expect in TypeScript.
Instead of representing an empty object, it represents any value except null
and undefined
.
This is because TypeScript's type system is structural, not nominal. Everything except null
and undefined
is an object, so everything can be assigned to an empty object.
If you want to represent an empty object, use Record<string, never>
instead.
The empty object type in TypeScript doesn't really behave as you expect. It doesn't represent "any object". Instead, it represents any value that isn't null
or undefined
.
ts
constexample1 : {} = "str";constexample2 : {} = 123;constexample3 : {} = true;constexample4 : {} = {foo : "whatever",};
Here, we are basically typing example1
as an empty object, yet we can pass a string to it. We can pass a number to it. We can pass a boolean to it and any object to it.
The only things we can't pass to it are null
or undefined
:
ts
constType 'null' is not assignable to type '{}'.2322Type 'null' is not assignable to type '{}'.: {} = null; example5 constType 'undefined' is not assignable to type '{}'.2322Type 'undefined' is not assignable to type '{}'.: {} = example6 undefined ;
If you're using ESLint, you might even hit an error:
Don't use
{}
as a type.{}
actually means "any non-nullish value".
This article explains exactly why this is good advice.
Object
TypeThis also happens with the Object
type, which I think is just an alias over the top of {}
:
ts
constobj1 :Object = "str";constType 'null' is not assignable to type 'Object'.2322Type 'null' is not assignable to type 'Object'.: obj2 Object = null;
So this behaves in exactly the same way. So you shouldn't be using this Object
type either.
If you do want to represent a kind of empty object, then we can use this Record<PropertyKey, never>
.
ts
typeEmptyObj =Record <PropertyKey , never>;constemptyObj1 :EmptyObj = {};constemptyObj2 :EmptyObj = {Type 'string' is not assignable to type 'never'.2322Type 'string' is not assignable to type 'never'.: "whatever", foo };constType 'string' is not assignable to type 'EmptyObj'.2322Type 'string' is not assignable to type 'EmptyObj'.: emptyObj3 EmptyObj = "str";constType 'number' is not assignable to type 'EmptyObj'.2322Type 'number' is not assignable to type 'EmptyObj'.: emptyObj4 EmptyObj = 123;
What this does is it means that you can pass an empty object. It's going to stop you from passing anything with a property on it. And it's also going to stop you from passing primitives, or null or undefined.
The only time this might be useful is if you want a really wide constraint on a function. Let's say you want to make sure that the thing that you're passing that function isn't null
or undefined
:
ts
constmyFunc = (constraint : {}) => {};myFunc ("str");myFunc (123);myFunc (true);
But even then, you're not going to be able to access any properties on constraint
:
ts
constmyFunc = (constraint : {}) => {Property 'foo' does not exist on type '{}'.2339Property 'foo' does not exist on type '{}'.constraint .; foo };
You'll hit this error:
Property 'foo' does not exist on type ''.
This is happening because we're trying to access a property on an empty object. So it's not terribly useful.
The only possible place it could be useful - or at least the one I want to cover here - is as a constraint in a generic function.
ts
constmyGenericFunc = <T extends {}>(t :T ) => {returnt ;};constresult1 =myGenericFunc ("str");constresult2 =myGenericFunc (123);constresult3 =myGenericFunc (true);
Inside myGenericFunc
, we want to make sure that we can't pass null
or undefined
into the generic function. If we hover over myGenericFunc
here, you can see that it's capturing str
and returning it in the results here.
But it fails when we try to pass in null or undefined.
ts
constArgument of type 'null' is not assignable to parameter of type '{}'.2345Argument of type 'null' is not assignable to parameter of type '{}'.result4 =myGenericFunc (null );constArgument of type 'undefined' is not assignable to parameter of type '{}'.2345Argument of type 'undefined' is not assignable to parameter of type '{}'.result5 =myGenericFunc (); undefined
So, you probably shouldn't be using the {}
type really anywhere. And you'll have likely come to this article because you found a linting rule that's preventing you from using it. That is why it doesn't represent what you think, but it's occasionally useful for constraining generics.
Share this article with your friends
Learn how to add TypeScript to your existing React project in a few simple steps.
Learn the essential TypeScript configuration options and create a concise tsconfig.json file for your projects with this helpful cheatsheet.
Big projects like Svelte and Drizzle are not abandoning TypeScript, despite some recent claims.
Learn different ways to pass a component as a prop in React: passing JSX, using React.ComponentType, and using React.ElementType.
Learn about TypeScript performance and how it affects code type-checking speed, autocomplete, and build times in your editor.
When typing React props in a TypeScript app, using interfaces is recommended, especially when dealing with complex intersections of props.