89

In typescript is there any way to assign a variable a generic object type. Here's what I mean by 'generic Object type'

let myVariable: GenericObject = 1 // Should throw an error
                              = 'abc' // Should throw an error
                              = {} // OK
                              = {name: 'qwerty'} //OK

i.e. It should only allow javascript objects to be assigned to the variable and no other type of data(number, string, boolean)

6 Answers 6

122

Sure thing:

type GenericObject = { [key: string]: any };

let myVariable1: GenericObject = 1; // Type 'number' is not assignable to type '{ [key: string]: any; }'
let myVariable2: GenericObject = 'abc'; // Type 'string' is not assignable to type '{ [key: string]: any; }'
let myVariable3: GenericObject = {} // OK
let myVariable4: GenericObject = {name: 'qwerty'} //OK

(code in playground)

Sign up to request clarification or add additional context in comments.

4 Comments

At least on TS 4.1.3 I get error TS2345: Argument of type 'GenericObject' is not assignable to parameter of type 'object'. when I try to pass it to new Proxy(). Any ideas?
@Romasato when i try that in the ts playground (ts 4.1.2) it works well, can you reproduce it there?
GenericObject here is the same as Record<string, any> You can also use Record<string, unknown> if you don't use any.
Only thing missing: in JS you can use a Symbol as index.
91

Typescript 2.1+ also has has a utility type, Record<K, T>, you can use instead of making your own definition

const myObj: Record<string, any>;

I like to use the style described in top answer when I can give a meaningful name to key but if it's not really as obvious or necessary Record is a great option.

1 Comment

This is the cleanest answer for a truly generic object. In my case I have an "erro" object consisting on an unknown number (0-?) of arbitrarily named strings, each being a assigned a value. Example: { "deviceSerialLength": "Device serial must be 20 characters long.\n", "deviceSerialFormat": "Device serial may only contain letters and numbers.\n" }
5

As of TypeScript 3.0+ this is the type-safe answer:

type GenericObject = Record<string, unknown>;

And since you'll be getting type protection, you need to do a type check before using a property of the object:

const obj: GenericObject = {
  someFn: () => 'string return';
}

if (typeof obj.someFn === 'function') {
  obj.someFn();
}

TypeScript will not complain about any of course but it is technically not the "generic object type".

More info on the differences between any and unknown:

Comments

1

Answer:

From ESLint hint:

  • If you want a type meaning "any object", you probably want object.

So the alternative is:

type GenericObject = object;

Or just use object directly where its needed. Note that letter casing is crucial since the Object type signifies "any non-nullish value" and might be discouraged.


Bonus:

This solution: type GenericObject = { [key: string]: unknown }; might not be the best option if you have a class data structure as it might lead to the errors:

Argument of type 'DummyClass' is not assignable to parameter of type 'GenericObject'.

Index signature for type 'string' is missing in type 'DummyClass'

Comments

0

A bit of a tangent since I haven't found a similar answer elsewhere, from @JaredMcAteer here, using record helped me with mixing enums + objects.

enum FOO_ENUM {
  BAR = 'BAZ';
}

type FOO_OBJECT_TYPE = { ... };

const BIZ_OBJECT: Record<FOO_ENUM, FOO_OBJECT_TYPE> = {
  [FOO_ENUM.BAR]: { ... }
}

Where before I was typing BIZ_OBJECT as
BIZ_OBJECT: {[type: string]: FOO_OBJECT}
which allowed something like BIZ_OBJECT.asd, now only a key from FOO_ENUM can be used, e.g.

  • BIZ_OBJECT.BAZ // { ... }
  • BIZ_OBJECT.asd // Property 'asd' does not exist on type ...
  • BIZ_OBJECT[FOO_ENUM.BAR] // { ... }
  • BIZ_OBJECT[FOO_ENUM.asd] // Property 'asd' does not ...
  • BIZ_OBJECT[FOO_ENUM['asd']] // ! not caught !

1 Comment

Besides the point, but I recommend naming your type like FooObjectType, and enum like FooEnum. Personally I find type name with ALL_CAPS too noisy.
-1

As of TypeScript 2.2 you can use

let myVariable: object;

Edit: Here's an example:

let myVariable: object = { fun: 1 };

5 Comments

this does not work eg: const me: object = {}; me.fun = 1 Will throw an error. Property 'fun' does not exist on type 'object'.
But you can do let myVariable: object = { fun: 1 }; which is what the OP asked.
The built-in object type also doesn't define an index signature, so it's not a very good placeholder for generic objects, unfortunately.
@BillBarnes nobody creates an object to leave it empty, OP is showing an empty object on line 3 therefore this is NOT what OP asked.
Don't use object for this. It exists for a different reason : to remove primitives : youtube.com/watch?v=0y5hhzuQjCA

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.