Objects

Objects are the fundamental way in which data and functions are grouped together and passed around.

An object is a comma-separate list of key-value pairs, enclosed in curly braces ({ and }). A key is a unique identifier, which can be used to efficiently look up a particular value.

object syntaxMultiple keys
{ "key1": value1 , "key2": value2 , ... }

To improve a program's readability, the key-value pairs will often be written on separate lines. We can use the dot operator (.) to retrieve the value corresponding to a particular key with obj.key.

Loading TypeScript...

In the example above, the person object has an implicit typing (i.e. one that TypeScript figured out) where the name and age key correspond to string and number. However, the TypeScript compiler will become especially useful when you formally tell it about the structure of objects being used in a program.

Interfaces

An interface declaration defines the key and value types of an object.

The main purpose of an interface is to tell the TypeScript compiler about how an object is structured. All interface declarations are removed by the compiler when it generates JavaScript code.

As interfaceMultiple key-value pairs
interface MyObject {
    
key: value
    
key2: value2
}

You can also use type to define the structure of an object - both examples are shown below.

Loading TypeScript...

Structural Compatibility

TypeScript uses structural typing to determine whether two types are compatible. This means that compatibility is based on the shape of the types — the properties they have and how they're typed — rather than their explicit declarations.

A Point3D object can be used wherever a Point2D is expected, because it has at least the required properties x and y. We also don't need to explicitly set the type of pt to Point3D - as long as the type inferred by TypeScript for pt matches Point3D, it is allowed to be used as a Point3D argument.

This flexibility allows for safer, more adaptable programs. However, it also means that TypeScript does not enforce nominal typing (i.e., type compatibility based on name), which can be surprising for developers coming from other statically typed languages.

Type Composition

The ampersand operator (&) can be used to construct an intersection type which contains all the members of two or more types.

Notice that the & actually joins the two types. More precisely, A & B creates a new type C such that every object that matches either A or B will fit the description of C. This is often useful for constructing a safe interface for operating with multiple other interfaces.

This also means that two overlapping keys that map to different value types will produce a resulting value of never.

The type of never indicates to any operations that depend on values of type C that the foo property should not be accessed. Consider the exact same example as above, but with the union type operator (|) instead.

If you played with the example above, you might have noticed that the union type will only contain keys that are in both object types.

Type composition

Let's look at a more practical example to understand the implications of all this. Suppose we have two functions for sending emails, and we want to merge them into one.

We have a roughly equivalent choice here of using Employee | Customer or Employee & Customer as the first parameter of our new composed function if we are only interested in the name and email field.

The difference is that by choosing a union type (|) instead, we being more restrictive with the information that the function has available to operate on. There are certainly cases where & is more appropriate, but in this case, we are telling the sendEmail function that a Recipient may have a badge and birthday property that can also be accessed.

Also notice what happens if we update the Customer type to include a required key. We can no longer send emails with Employee objects, because the intersection type also contains a required key that Employee objects do not have.

Loading TypeScript...

Indexed Types

The value type for a specific key can be extracted with an indexed access type.

The indexing type is itself a type, so we can use unions keyof, or other types.

You will even get an error if you try to access a property which doesn't exist.

Optional properties

Add a ? after a property name to mark it as optional.

When a property is optional, it either has a value of undefined or is not present in an object's keys. Keep in mind that undefined is different from null, so you would need to specifically include null to include it in the type.

Loading TypeScript...

If a key is not marked as optional, it is required. An object that is missing a required key will generate a TypeScript error.

Partial and Required

The Partial type produces a new object type where all properties are optional, and Required produces an object type where all properties are required.

The Partial<T> specification is often used when working with a subset of an object's properties. In the example below, a simple updateUser function allows a partial set of user properties to be updated.

We can combine Partial and Required to build a type where some keys are optional, and others are required. Suppose we want to store all color definitions properties in a single Color interface:

Readonly

A readonly property can only be initialized once.

Loading TypeScript...
Loading TypeScript...

Index signatures

Index signatures allow you to define the shape of objects that may have unknown or dynamically named properties.

This is useful when dealing with objects where property names are not predetermined but share a common value type.

Loading TypeScript...

The

Loading TypeScript...

It is

Mapped types

A mapped type iterates through a set of keys to create a new type.

You can apply a ? modifier during mapping to make the mapped values optional, or use a -? to make the mapped value required.

If you are using a more recent version of TypeScript, you can re-map keys in mapped types with an as clause.

You can also filter out keys by conditionally producing the never type.

You can map over arbitrary unions, not just string | number | symbol.

Mapped types can be useful in enforcing compiler guarantees based on the data and constants in your programs.

Object from tuples

The Object.fromEntries function produces an object from an array of tuples, where the first element of each tuple is the key and the second element is the value.

Loading TypeScript...

Note that Object.fromEntries requires your TypeScript compiler target to be set to ES2019 or later.

Was this page helpful?