Skip to Content
TypeScript for Beginners: Catch Wrong Data Before It Runs
Beginner
0 likes
Courses
TypeScript for Beginners: Catch Wrong Data Before It Runs

Optional, Readonly and Why any Is a Trap

Mark properties optional or readonly, handle the missing case, and see how any switches the checks off. After this, you reach for unknown instead of any.

Course content

Sign in to save your progress.

Course Home

Skip ahead: If you already use ?, readonly and know why any switches checking off, skip to "Why any is a trap", then continue.

Some properties are not always there. Some should never change after you create them. And sometimes you will be tempted to type something as any to make a red line go away. This lesson covers the first two tools, then explains why the third is the one to avoid.

Optional properties

Add ? after the name when the property may be missing.

type Product = {
  title: string;
  subtitle?: string;
};

const pen: Product = { title: "Pen" };
console.log(pen.subtitle.toUpperCase());
optional.ts(7,13): error TS18048: 'pen.subtitle' is possibly 'undefined'.

subtitle?: string means the property is either a string or undefined. The compiler remembers that and stops you from calling a string method on something that may not exist. Without this check, that line crashes at run time with "Cannot read properties of undefined".

You have to handle the missing case first. Two common ways:

type Product = {
  title: string;
  subtitle?: string;
};

const pen: Product = { title: "Pen" };

if (pen.subtitle !== undefined) {
  console.log(pen.subtitle.toUpperCase());
}

console.log(pen.subtitle ?? "No subtitle");
console.log(pen.subtitle?.length);
No subtitle
undefined

The if check narrows the type inside the block. ?? supplies a default when the value is undefined or null. ?. stops and gives undefined instead of crashing. Pick the one that matches what you want to happen when the value is missing.

readonly

Mark a property readonly when it must not change after you create the object.

type Invoice = {
  readonly id: number;
  amount: number;
};

const invoice: Invoice = { id: 7, amount: 500 };
invoice.amount = 450;
invoice.id = 8;

const steps: readonly string[] = ["draft", "sent"];
steps.push("paid");
readonly.ts(8,9): error TS2540: Cannot assign to 'id' because it is a read-only property.
readonly.ts(11,7): error TS2339: Property 'push' does not exist on type 'readonly string[]'.

Changing amount is fine. Changing id is refused. A readonly string[] has no push, so the last line fails too. This is checked only while you compile. At run time, JavaScript will still let the property change if something slips past.

💡 Tip: Use readonly for ids and for arrays a function should only read. It is a cheap way to say "do not touch this".

Why any is a trap

any is a type that turns checking off. A value typed any can be used as a number, a string, a function or nothing at all, and the compiler says nothing.

const data: any = JSON.parse('{"count": "three"}');
const count: number = data.count;

try {
  console.log(count.toFixed(1));
} catch (e) {
  console.log((e as Error).message);
}
count.toFixed is not a function

There is not one error. The variable count claims to be a number, and it holds the string "three". any is not just unsafe in the place you write it. It spreads, because everything you copy out of an any value is accepted without a check.

The tempting move is to write any when an error is hard to read. Generally, that trades one visible problem for a hidden one. You do not fix the mistake. You only stop being told.

What to write instead

If you know the shape, write it, as in the earlier lessons. If you truly do not know, use unknown. It accepts anything but makes you check before you use it.

const input: unknown = "42";
console.log(input.toUpperCase());

if (typeof input === "string") {
  console.log(input.toUpperCase());
}
unknown.ts(2,13): error TS18046: 'input' is of type 'unknown'.

The first call is refused. The call inside the typeof check is fine, because the compiler now knows it is a string. That is the same narrowing you will use on unions in lesson 9.

Common mistakes

  • Typing a function parameter as any to stop an implicit-any error. Write the real type.
  • Using as to claim a value has a type you have not checked.
  • Making every property optional "just in case". Then every use needs a check, and the shape tells the reader nothing.

If you skip this, any will creep in one quick fix at a time, and in a few weeks you will have a typed project that checks very little. Where in your own code would an any hide a bug right now?

About

Mark properties optional or readonly, handle the missing case, and see how any switches the checks off. After this, you reach for unknown instead of any.

Duration
5 min
Last update
10/03/2026
Was this helpful?
​ 0 ​ 0

Comments (0)

Sign in to comment

No comments yet.