All Articles

Why You Can't Use Dot Notation On Types

Matt Pocock
Matt PocockMatt is a well-regarded TypeScript expert known for his ability to demystify complex TypeScript concepts.

In TypeScript, it's common to feel frustration that you can't use dot notation to access type properties.

ts
type Person = {
name: string;
age: number;
};
 
type Name = Person.name;
Cannot access 'Person.name' because 'Person' is a type, but not a namespace. Did you mean to retrieve the type of the property 'name' in 'Person' with 'Person["name"]'?2713Cannot access 'Person.name' because 'Person' is a type, but not a namespace. Did you mean to retrieve the type of the property 'name' in 'Person' with 'Person["name"]'?

But there are several good reasons the TypeScript team hasn't implemented it.

Quick Explanation

  • Square bracket notation can do everything that dot notation can do, and more:
ts
type Person = {
name: string;
age: number;
};
 
type Name = Person["name"];
type Name = string
 
type AllValues = Person["name" | "age"];
type AllValues = string | number
  • The TypeScript team is unlikely to add new syntax for a feature that's already possible another way.

Full Explanation

You're used to two different syntaxes in JavaScript for accessing property values:

ts
const person = {
name: "Ada",
age: 42,
};
 
// Dots
const name = person.name;
 
// Square brackets
const age = person["age"];

So why does TypeScript only allow the second syntax?

ts
type Person = {
name: string;
age: number;
};
 
type PersonName = Person.name;
Cannot access 'Person.name' because 'Person' is a type, but not a namespace. Did you mean to retrieve the type of the property 'name' in 'Person' with 'Person["name"]'?2713Cannot access 'Person.name' because 'Person' is a type, but not a namespace. Did you mean to retrieve the type of the property 'name' in 'Person' with 'Person["name"]'?
 
type PersonAge = Person["age"];

TypeScript Namespaces

The error discovered above is usefully phrased.

'Person' is a type, but not a namespace.

TypeScript namespaces are a way of grouping together types and values into a single spot.

And unlike types, they allow you to use the dot notation OR square bracket notation to access their members.

ts
namespace MyMath {
export const PI = 3.14;
 
export type Vector = {
x: number;
y: number;
};
}
 
const pi = MyMath["PI"];
 
type Vector = MyMath.Vector;

Namespaces are, in general, out of favor. They were brought in as a potential solution to 'modules' in JavaScript before ES Modules came along - so they're a legacy feature.

Namespaces also compile to runtime code. The code above will end up looking like this:

var MyMath;

(function (MyMath) {
  MyMath.PI = 3.14;
})(MyMath || (MyMath = {}));

const pi = MyMath["PI"];

So - namespaces are really just objects, and objects can be accessed with dot notation or square bracket notation.

All this to say - we now understand the error we were getting before. But why is it happening in the first place?

What makes types different?

Let's take another look at our property access syntax:

ts
type Person = {
name: string;
age: number;
};
 
type Name = Person["name"];

It's important to remember that this wasn't always available in TypeScript.

Specifically, it's called Indexed Access Types, and it landed in TypeScript 2.1 in 2016.

When the TypeScript team ships a new feature, they tend towards minimum syntactical impact. In other words - the fewest possible syntaxes for doing the same thing.

So, when they added the ability to access properties on types, they only chose one syntax - square bracket notation.

Why not both?

Square bracket notation is far more flexible than dot notation. For example, it lets you accept unions:

ts
type Person = {
name: string;
age: number;
};
 
type Values = Person["name" | "age"];
type Values = string | number

Or even another type - perhaps extracted from the target itself. A popular pattern is to pass keyof to an indexed access type:

ts
// Simple Object values!
type Values2 = Person[keyof Person];
type Values2 = string | number

A version of this with dot notation would be hard to imagine:

type Person = {
  name: string;
  age: number;
};

// Bleugh
type Values = Person.(keyof Person);

So - the TypeScript team chose the most flexible syntax and left it at that.

Will we ever get dot notation for types?

The TypeScript team is very careful about adding new syntax to the language, and they're unlikely to add new syntax for a feature that's already possible.

Remco Haszing on Twitter also pointed out that bringing in this syntax would conflict with the namespace syntax:

TypeScript lets you declare an interface with the same name as a namespace - meaning the access notation acts as a differentiator between the type and the namespace.

ts
namespace MyMath {
export type PI = 3.14;
}
 
interface MyMath {
PI: number;
}
 
// Resolves to the type
type Pi = MyMath["PI"];
type Pi = number
 
// Resolves to the namespace
type Pi2 = MyMath.PI;
type Pi2 = 3.14

Gross - but this is the kind of thing that the TypeScript team has to consider when adding new syntax.

So, it's highly unlikely. We'll be stuck with our square bracket notation for a while yet.

Matt's signature

Share this article with your friends

`any` Considered Harmful, Except For These Cases

Discover when it's appropriate to use TypeScript's any type despite its risks. Learn about legitimate cases where any is necessary.

Matt Pocock
Matt Pocock

No, TypeScript Types Don't Exist At Runtime

Learn why TypeScript's types don't exist at runtime. Discover how TypeScript compiles down to JavaScript and how it differs from other strongly-typed languages.

Matt Pocock
Matt Pocock

Deriving vs Decoupling: When NOT To Be A TypeScript Wizard

In this book teaser, we discuss deriving vs decoupling your types: when building relationships between your types or segregating them makes sense.

Matt Pocock
Matt Pocock

NoInfer: TypeScript 5.4's New Utility Type

Learn how TypeScript's new utility type, NoInfer, can improve inference behavior by controlling where types are inferred in generic functions.

Matt Pocock
Matt Pocock