Type definition in object literal in TypeScript
TypescriptTypescript Problem Overview
In TypeScript classes it's possible to declare types for properties, for example:
class className {
property: string;
};
How do declare the type of a property in an object literal?
I've tried the following code but it doesn't compile:
var obj = {
property: string;
};
I'm getting the following error:
> The name 'string' does not exist in the current scope
Am I doing something wrong or is this a bug?
Typescript Solutions
Solution 1 - Typescript
You're pretty close, you just need to replace the =
with a :
. You can use an object type literal (see spec section 3.5.3) or an interface. Using an object type literal is close to what you have:
var obj: { property: string; } = { property: "foo" };
But you can also use an interface
interface MyObjLayout {
property: string;
}
var obj: MyObjLayout = { property: "foo" };
Solution 2 - Typescript
Update 2019-05-15 (Improved Code Pattern as Alternative)
After many years of using const
and benefiting from more functional code, I would recommend against using the below in most cases. (When building objects, forcing the type system into a specific type instead of letting it infer types is often an indication that something is wrong).
Instead I would recommend using const
variables as much as possible and then compose the object as the final step:
const id = GetId();
const hasStarted = true;
...
const hasFinished = false;
...
return {hasStarted, hasFinished, id};
- This will properly type everything without any need for explicit typing.
- There is no need to retype the field names.
- This leads to the cleanest code from my experience.
- This allows the compiler to provide more state verification (for example, if you return in multiple locations, the compiler will ensure the same type of object is always returned - which encourages you to declare the whole return value at each position - giving a perfectly clear intention of that value).
Addition 2020-02-26
If you do actually need a type that you can be lazily initialized: Mark it is a nullable union type (null or Type). The type system will prevent you from using it without first ensuring it has a value.
In tsconfig.json
, make sure you enable strict null checks:
"strictNullChecks": true
Then use this pattern and allow the type system to protect you from accidental null/undefined access:
const state = {
instance: null as null | ApiService,
// OR
// instance: undefined as undefined | ApiService,
};
const useApi = () => {
// If I try to use it here, the type system requires a safe way to access it
// Simple lazy-initialization
const api = state?.instance ?? (state.instance = new ApiService());
api.fun();
// Also here are some ways to only access it if it has value:
// The 'right' way: Typescript 3.7 required
state.instance?.fun();
// Or the old way: If you are stuck before Typescript 3.7
state.instance && state.instance.fun();
// Or the long winded way because the above just feels weird
if (state.instance) { state.instance.fun(); }
// Or the I came from C and can't check for nulls like they are booleans way
if (state.instance != null) { state.instance.fun(); }
// Or the I came from C and can't check for nulls like they are booleans
// AND I was told to always use triple === in javascript even with null checks way
if (state.instance !== null && state.instance !== undefined) { state.instance.fun(); }
};
class ApiService {
fun() {
// Do something useful here
}
}
Do not do the below in 99% of cases:
Update 2016-02-10 - To Handle TSX (Thanks @Josh)
Use the as
operator for TSX.
var obj = {
property: null as string
};
A longer example:
var call = {
hasStarted: null as boolean,
hasFinished: null as boolean,
id: null as number,
};
Original Answer
Use the cast operator to make this succinct (by casting null to the desired type).
var obj = {
property: <string> null
};
A longer example:
var call = {
hasStarted: <boolean> null,
hasFinished: <boolean> null,
id: <number> null,
};
This is much better than having two parts (one to declare types, the second to declare defaults):
var callVerbose: {
hasStarted: boolean;
hasFinished: boolean;
id: number;
} = {
hasStarted: null,
hasFinished: null,
id: null,
};
Solution 3 - Typescript
I'm surprised that no-one's mentioned this but you could just create an interface called ObjectLiteral
, that accepts key: value
pairs of type string: any
:
interface ObjectLiteral {
[key: string]: any;
}
Then you'd use it, like this:
let data: ObjectLiteral = {
hello: "world",
goodbye: 1,
// ...
};
An added bonus is that you can re-use this interface many times as you need, on as many objects you'd like.
Good luck.
Solution 4 - Typescript
You could use predefined utility type Record<Keys, Type>
:
const obj: Record<string, string> = {
property: "value",
};
It allows to specify keys for your object literal:
type Keys = "prop1" | "prop2"
const obj: Record<Keys, string> = {
prop1: "Hello",
prop2: "Aloha",
something: "anything" // TS Error: Type '{ prop1: string; prop2: string; something: string; }' is not assignable to type 'Record<Keys, string>'.
// Object literal may only specify known properties, and 'something' does not exist in type 'Record<Keys, string>'.
};
And a type for the property value:
type Keys = "prop1" | "prop2"
type Value = "Hello" | "Aloha"
const obj1: Record<Keys, Value> = {
prop1: "Hello",
prop2: "Hey", // TS Error: Type '"Hey"' is not assignable to type 'Value'.
};
Solution 5 - Typescript
If you're trying to write a type annotation, the syntax is:
var x: { property: string; } = { property: 'hello' };
If you're trying to write an object literal, the syntax is:
var x = { property: 'hello' };
Your code is trying to use a type name in a value position.
Solution 6 - Typescript
If you're trying to add typings to a destructured object literal, for example in arguments to a function, the syntax is:
function foo({ bar, baz }: { bar: boolean, baz: string }) {
// ...
}
foo({ bar: true, baz: 'lorem ipsum' });
Solution 7 - Typescript
In TypeScript if we are declaring object then we'd use the following syntax:
[access modifier] variable name : { /* structure of object */ }
For example:
private Object:{ Key1: string, Key2: number }
Solution 8 - Typescript
// Use ..
const Per = {
name: 'HAMZA',
age: 20,
coords: {
tele: '09',
lan: '190'
},
setAge(age: Number): void {
this.age = age;
},
getAge(): Number {
return age;
}
};
const { age, name }: { age: Number; name: String } = Per;
const {
coords: { tele, lan }
}: { coords: { tele: String; lan: String } } = Per;
console.log(Per.getAge());
Solution 9 - Typescript
This is what I'm doing in 2021 with TypeScript 4.5:
const sm = {
reg: {} as ServiceWorkerRegistration,
quantum: null as number | null,
currentCacheName: '' as string, // superfluous
badSWTimer: 0 as number, // superfluous
}
This is not just a value cast, but works the same as an interface definition, for the object properties that is.
Update: I included two superfluous typings as an example. That is, these typings can be inferred automatically and thus, would not generate compiler errors.
Source: 4.4 Playground
Solution 10 - Typescript
In your code:
var obj = {
myProp: string;
};
You are actually creating a object literal and assigning the variable string to the property myProp. Although very bad practice this would actually be valid TS code (don't use this!):
var string = 'A string';
var obj = {
property: string
};
However, what you want is that the object literal is typed. This can be achieved in various ways:
Interface:
interface myObj {
property: string;
}
var obj: myObj = { property: "My string" };
Type alias:
type myObjType = {
property: string
};
var obj: myObjType = { property: "My string" };
Object type literal:
var obj: { property: string; } = { property: "Mystring" };
Solution 11 - Typescript
Beware. It may seem obvious to some, but the type declaration:
const foo: TypeName = {}
is not the same compared to casting with as
:
const foo = {} as TypeName
despite the suggestions to use it on other answers.
Example:
Thanks, type-safety!:
const foo: { [K in 'open' | 'closed']: string } = {}
// ERROR: TS2739: Type '{}' is missing the following properties from type '{ open: string; closed: string; }': open, closed
Goodbye, type-safety!:
const foo = {} as { [K in 'open' | 'closed']: string }
// No error
Solution 12 - Typescript
- create a type using type keyword
type ObjType = {
property: string;
}
and then you can use it to bind your object to accept this type only, like below.
const obj: ObjType = {
property: "TypeScript"
}
Solution 13 - Typescript
Just to extend @RickLove's reply...
This works great, as you only need to define the type that cannot be inferred:
const initialState = {
user: undefined as User | undefined,
userLoading: false
};
and it transpiles to this js code:
const initialState = {
user: undefined,
userLoading: false
};
And if you need to extract it into a type, you can just do this:
export type InitState = typeof initialState;
Solution 14 - Typescript
Convert Object Literal into Type With DRY
Just do:
const myObject = {
hello: 'how are you',
hey: 'i am fine thank you'
}
type myObjectType = keyof typeof MyObject
Job done!