Angular – The TypeScript You Need First

June 3, 20263 min readUpdated 8/21/2026

Angular is TypeScript-first. You do not need to be an expert, and there is a short list of features it leans on constantly — which is not quite the list a general TypeScript tutorial covers.

Interfaces for API shapes

This is the highest-value TypeScript in any application. One file describing what the server returns:

export interface ProductSize {
  id: UUID;
  size: SizeName;
  price: number;
}

The demo app keeps all of them in core/models.ts, mirroring the backend's DTOs exactly. If the shapes drift, TypeScript fails the build instead of the UI failing at runtime — which is the entire argument for typing an API boundary.

Union types instead of enums

export type ProductType = 'PIZZA' | 'DRINK';
export type SizeName = 'SMALL' | 'MEDIUM' | 'LARGE';
export type ToppingCategory = 'MEAT' | 'VEGGIE' | 'CHEESE';
export type OrderType = 'DELIVERY' | 'CARRYOUT';

A string union gives you autocomplete and a compile error on a typo, and it is exactly the JSON the server sends. A TypeScript enum generates a runtime object and a value that is not the string, which then needs converting at the boundary. Prefer the union.

Type aliases that carry intent

export type UUID = string;

Structurally this is just string. Its value is documentation at every use site: it is a UUID, not "some string". The comment on that line in the app explains why the API exposes UUIDs at all — sequential ids would let anyone walk /api/orders/1, /2, /3 and read other people's orders.

Generics

get<T>(path: string): Observable<T> {
  return this.http.get<T>(path);
}

<T> is a type the caller chooses. api.get<Product[]>('/api/products') returns Observable<Product[]> — one method, correctly typed for every endpoint.

You will mostly use generics rather than write them, and they are everywhere in Angular: input<Filter>(), output<Product>(), signal<CartItem[]>([]).

Decorators

@Component({
  selector: 'app-spinner',

A decorator attaches metadata to a class. @Component, @Directive, @Injectable and @Pipe are the four you will meet, and they are how Angular knows a class is a component rather than an ordinary class.

Notably, the member decorators are on the way out: @Input() and @Output() have been replaced by the input() and output() functions. Class decorators are staying.

strict mode

"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,

New projects get this by default. The part that changes how you write code is strictNullChecks: User | null is not User, and the compiler makes you handle the difference.

That is a feature. Most runtime errors in a frontend are something being null when you assumed it was not, and this converts them into build errors.

readonly displayName = computed(() => this._user()?.fullName ?? this._user()?.email ?? 'Account');

?. stops at null; ?? supplies a fallback for null or undefined specifically — unlike ||, which also fires for 0 and the empty string. In an application full of prices and quantities, that distinction is a real bug rather than a nicety.

Avoid the escape hatches

as any and the non-null assertion ! both silence the compiler without changing the runtime. They have their uses — the template's $any($event.target) is one — but each is a place you have promised something the compiler could not verify.

readonly

private readonly api = inject(ApiService);
readonly items = this._items.asReadonly();

Nearly every field in the demo app is readonly, and the habit is worth copying. An injected service is never reassigned; neither is a signal — you call set() on it, which is not reassignment. Marking them says so and makes an accidental reassignment a compile error.

What is next

The first real Angular concept: components.