Skip to content

Type Aliases

In Module 02 you wrote types inline — string | number, { text: string; answer: string }, 'easy' | 'medium' | 'hard'. Those work, but inline types do not have names. When the same shape appears in three function signatures, you copy it three times. When it changes, you change it in three places.

The type keyword gives any type a name.

type Difficulty = 'easy' | 'medium' | 'hard';

Now Difficulty is a named type you can use anywhere:

function buildUrl(amount: number, difficulty: Difficulty): string {
return `https://opentdb.com/api.php?amount=${amount}&difficulty=${difficulty}`;
}
function displayDifficulty(d: Difficulty): void {
console.log('Difficulty:', d);
}
let level: Difficulty = 'easy';

If the allowed values ever change — say, adding 'any' as an option — you update one line in the type declaration and every usage is covered automatically.

You can alias any type, including primitives:

type Score = number;
type QuestionText = string;

These are purely for documentation — TypeScript treats Score and number as identical types and does not enforce them separately. For this reason, primitive aliases are uncommon. Use them only when they meaningfully communicate intent to readers of the code.

Union types are where type aliases shine most. Without an alias, a union that appears in multiple places becomes noise:

// Without alias — repeated, fragile
function start(difficulty: 'easy' | 'medium' | 'hard'): void {}
function validate(d: 'easy' | 'medium' | 'hard'): boolean { return true; }

With an alias:

type Difficulty = 'easy' | 'medium' | 'hard';
function start(difficulty: Difficulty): void {}
function validate(d: Difficulty): boolean { return true; }

The intent is named, the constraint is defined once, and both functions stay in sync automatically.

type can describe object shapes just like an interface (covered in the next lesson). The syntax uses = and a type expression:

type HighScore = {
score: number;
total: number;
date: string;
};
const best: HighScore = { score: 8, total: 10, date: 'Jan 15, 2024' };

This is equivalent to what you wrote inline in Module 02 — the difference is the name. HighScore can now be imported, reused, and updated in one place.

AceIt’s ui.ts uses a local type alias to constrain the showScreen function’s parameter:

type ScreenId = 'start-screen' | 'loading-screen' | 'question-screen' | 'result-screen';
export function showScreen(id: ScreenId): void {
document.querySelectorAll<HTMLElement>('.screen').forEach(el => { el.hidden = true; });
const screen = document.getElementById(id);
if (screen) screen.hidden = false;
}

Without ScreenId, calling showScreen('quiz-screen') — a screen that does not exist — would only fail at runtime. With the alias, it is a compile error. The string 'quiz-screen' is not assignable to ScreenId.

type can describe anything — not just objects and unions:

type StringPair = [string, string];
type Callback = (score: number, total: number) => void;
type NullableQuestion = TriviaQuestion | null;

Interfaces (next lesson) can only describe object shapes. type is the only option for tuples, function signatures, union types, and combinations of these.

In types.ts in your aceit/ folder, write the following type aliases:

  1. type Difficulty = 'easy' | 'medium' | 'hard' — you already wrote this; confirm it is exported.
  2. type ScreenId = 'start-screen' | 'loading-screen' | 'question-screen' | 'result-screen'.
  3. type HighScore = { score: number; total: number; date: string }.
  4. type NullableHighScore = HighScore | null — used as the return type for loadHighScore.
  • type Name = ... gives any type a name — primitives, unions, tuples, object shapes, or function signatures.
  • Named types make signatures readable and keep related constraints in sync.
  • type aliases for primitives are purely documentary — TypeScript treats them as identical to the underlying type.
  • type is the only option for naming unions, tuples, function types, and intersections.
  • Object shapes can be named with either type or interface — the next lesson covers the difference.