Skip to content

MODELING DATA · LESSON 5 OF 24

Objects, interfaces and type aliases

Define reusable object contracts and understand structural compatibility.

  • interface
  • type
  • optional
  • structural typing

Your runnable checkpoint

Extract into a new practice folder. Each download includes complete source, package.json, tsconfig.json and an offline lesson. Use Node.js 22 or 24. Each checkpoint is independent.

Install with npm install. Run npm run check, then npm run build. Terminal lessons use npm start; browser lessons use npm run serve and http://127.0.0.1:4173.

1. Understand the concepts

An interface describes an object shape and can extend another interface. A type alias can name an object, union, tuple or other type expression. Both are useful; pick a consistent style and choose based on the relationship you need to express.

TypeScript compares most object types structurally: a value is compatible when it has the required members with compatible types. It does not require an explicit declaration that an object implements an interface.

An optional property may be absent. With exactOptionalPropertyTypes enabled, email?: string permits omission but not an explicitly assigned undefined value unless undefined is included in the type. Reading it can still produce undefined.

Excess-property checks help catch misspellings on fresh object literals. They are not a general runtime validator and do not imply that compatible objects have no additional fields. readonly restricts writes through a type, not runtime mutation everywhere.

2. Build and run

These are complete files. Preserve the paths shown below, including the src directory. The package and compiler configuration are included at the end.

src/main.ts

src/main.ts
interface Person {
  readonly id: number;
  name: string;
  email?: string;
}
interface Student extends Person {
  course: string;
}
type StudentCard = Pick<Student, "name" | "course">;
function label(person: Person): string {
  return `${person.name} (${person.email ?? "no email"})`;
}
const student: Student = { id: 1, name: "Asha", course: "TypeScript" };
const card: StudentCard = { name: student.name, course: student.course };
console.log(label(student));
console.log(card.course);

package.json

package.json
{
  "name": "edupoly-typescript-objects-interfaces-and-aliases",
  "private": true,
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "check": "tsc --noEmit",
    "build": "tsc",
    "start": "node dist/main.js"
  },
  "devDependencies": {
    "typescript": "5.9.3"
  }
}

tsconfig.json

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": [
      "ES2022",
      "DOM"
    ],
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noEmitOnError": true,
    "verbatimModuleSyntax": true,
    "rootDir": "src",
    "outDir": "dist",
    "sourceMap": true,
    "types": [],
    "moduleDetection": "force"
  },
  "include": [
    "src/**/*"
  ]
}

3. Trace the code and check the result

  1. Student inherits the Person members.
  2. label accepts a Student because it contains the Person shape.
  3. Pick creates a smaller view; it does not remove fields from an existing object at runtime.

Expected result

Asha (no email)
TypeScript

4. Practice independently

Add an optional graduationYear and print a fallback when it is absent.

Show the explanation

Declare graduationYear?: number. Display student.graduationYear ?? "Not set". Do not assign undefined unless you explicitly add it to the property type.

When something goes wrong

If tsc is missing, run npm install in the folder containing package.json. If the output looks old, rebuild before running. Read the first compiler diagnostic and check the indicated field or argument. For module errors, preserve the .js extensions in imports. Browser examples must run through the included local server, not directly as TypeScript files.

Read the related official TypeScript documentation ↗