DEV Community

Julian Garamendy
Julian Garamendy

Posted on • Edited on • Originally published at juliangaramendy.dev

Readonly<T> and Better Error Messages

A few weeks ago I learned something about TypeScript errors and utility types.

The following is true in TypeScript v3.7.5. In my experience, error messages in TS improve a lot with each release, so this may soon be irrelevant.

As usual, here's an example with bananas.

I had this Banana type:

type Banana = {
  id: number;
  name: string;
  color: number;
  weight?: number;
  length?: number;
  thumbnail?: string;
  pictures?: Array<string>;
};
Enter fullscreen mode Exit fullscreen mode

I was trying to be clever and I thought I would make my type immutable by using the Readonly utility type like this:

type Banana = Readonly<{
  id: number;
  name: string;
  color: number;
  weight?: number;
  length?: number;
  thumbnails?: string;
  pictures?: ReadonlyArray<string>;
}>;
Enter fullscreen mode Exit fullscreen mode

But this turned out not to be such a great idea.

I had made a (Readonly)Map with a few bananas:

const bananaMap: ReadonlyMap<number, Banana> = new Map([
  [1, { id: 1, name: "yellow banana", color: 0xffff00 }],
  [2, { id: 2, name: "red banana", color: 0xff0000 }],
  [3, { id: 3, name: "green banana", color: 0x00ff00 }]
]);
Enter fullscreen mode Exit fullscreen mode

Then I tried to access an element in this way:

const banana: Banana = bananaMap.get(1);
Enter fullscreen mode Exit fullscreen mode

And I got an error on the banana identifier which looked like this:

Type 'Readonly<{ id: number; name: string; color: number; weight?: number 
| undefined; length?: number | undefined; thumbnails?: string | undefined;
 pictures?: readonly string[] | undefined; }> | undefined' is not 
assignable to type 'Readonly<{ id: number; name: string; color: number; 
weight?: number | undefined; length?: number | undefined; thumbnails?: 
string | undefined; pictures?: readonly string[] | undefined; }>'.
  Type 'undefined' is not assignable to type 'Readonly<{ id: number; 
name: string; color: number; weight?: number | undefined; length?: 
number | undefined; thumbnails?: string | undefined; pictures?: 
readonly string[] | undefined; }>'.
Enter fullscreen mode Exit fullscreen mode

It took me a while to understand what the problem was. The important part was on the second "paragraph" and I had to scroll down to find it, and still stare at it for a while.

You can see this example in the TypeScript Playground

So I changed the type, marking each field as readonly instead:

type Banana = {
  readonly id: number;
  readonly name: string;
  readonly color: number;
  readonly weight?: number;
  readonly length?: number;
  readonly thumbnails?: string;
  readonly pictures?: ReadonlyArray<string>;
};
Enter fullscreen mode Exit fullscreen mode

Then the error message looked a bit better:

Type 'Banana | undefined' is not assignable to type 'Banana'.
  Type 'undefined' is not assignable to type 'Banana'.
Enter fullscreen mode Exit fullscreen mode

And the issue was evident! The get method in the Map class returns an element or undefined. That means we can't annotate const banana with the Banana type.

const banana: Banana = bananaMap.get(1); // ❌ error!
Enter fullscreen mode Exit fullscreen mode

Some possible fixes:

// 1
const banana: Banana | undefined = bananaMap.get(1); // ✅ no error

// 2
const banana: Banana = bananaMap.get(1)!; // ⚠️ no error, but (*)
Enter fullscreen mode Exit fullscreen mode

* I would avoid the non-null assertion operator when possible.

You can see this new example in the TypeScript Playground.

UPDATE:

My friend Albert pointed our that we can get the "nice and short" error message mentioning out Banana type if we use interface instead of type:

interface Banana extends Readonly<{
  id: number;
  name: string;
  color: number;
  weight?: number;
  length?: number;
  thumbnails?: string;
  pictures?: ReadonlyArray<string>;
}> { }
Enter fullscreen mode Exit fullscreen mode

You can see this last example in TypeScript Playground


I would love to hear what you use to declare immutability in your code.

Please comment!


Photo by v2osk on Unsplash

Top comments (0)