AI InterviewSearch…Search problems, topics, or pages…Ctrl KGet started
Skip to content

13 TypeScript preview / Checkpoint

Checkpoint — TypeScript preview

24 min · +25 XP · 3 exercises · 12 questions

Exercises0/3
Quiz · 70% to clear12 q

This checkpoint covers erased types and inference, literal types and widening, any/unknown/never; object types, interface versus type, structural typing and excess-property checks, intersections, discriminated unions and satisfies; function types, generics with constraints, keyof/indexed access and the utility types; narrowing, exhaustiveness, type guards, assertion functions and the escape hatches; and the toolchain — tsconfig, declaration files, builds, migration, runtime validation.

How it works. Twelve questions and three programs; 70% on the questions and every program accepted clears the module. The programs are JavaScript (the judge runs Node) that model the type system's own rules — inference and widening, structural assignability, utility types over schemas, narrowing — so you reason about what the compiler would do.

Before you start, make sure you can answer:

  • What const x = "a" versus let x = "a" infers; what as const does; the difference between any, unknown and never.
  • Why a class name does not matter for assignability; when excess properties are an error; what a discriminated union's tag is for.
  • What K extends keyof T and T[K] express; how Omit is built; what a mapped type is.
  • Which checks narrow; why typeof x === "object" keeps null; what x is T and asserts x is T do; when as is acceptable.
  • What strict turns on; what a .d.ts is; why tsc --noEmit must run in CI when esbuild builds; how to migrate JavaScript.

The programs are a schema builder that both validates values and prints the TypeScript type it would infer, an exhaustive state machine over discriminated-union states and events, and a declaration-file generator that infers .d.ts text from runtime values.

Common questions

What is the difference between any, unknown and never?

any accepts every value and disables checking wherever it is used; unknown accepts every value but must be narrowed before use; never has no values at all and marks functions that never return and unions that have been exhausted.

Why does a class name not matter for assignability in TypeScript?

TypeScript is structurally typed: assignability depends on the shape — the properties a value has — not on the name it was declared with. Two classes with the same public members are interchangeable unless they have private members.

Why must tsc --noEmit run in CI when esbuild builds the code?

esbuild strips TypeScript types without checking them, so a build can succeed with type errors in it. A separate tsc --noEmit step in CI is what actually type-checks the project and stops those errors shipping.

TypeScript Quiz: Generics, Narrowing and Utility Types Test

Test your TypeScript with 12 questions and three programs on inference, any and unknown, structural typing, discriminated unions, generics and narrowing.

  • Course: JavaScript study plan
  • Module: TypeScript preview
  • Kind: Checkpoint — cleared at 70%
  • Reading time: 24 min
  • Runtime: Node 16

Checkpoint — TypeScript preview is the checkpoint that closes the TypeScript preview module: a graded quiz and whole-program exercises, passed at 70%.

Instructions

This checkpoint covers erased types and inference, literal types and widening, any/unknown/never; object types, interface versus type, structural typing and excess-property checks, intersections, discriminated unions and satisfies; function types, generics with constraints, keyof/indexed access and the utility types; narrowing, exhaustiveness, type guards, assertion functions and the escape hatches; and the toolchain — tsconfig, declaration files, builds, migration, runtime validation.

How it works. Twelve questions and three programs; 70% on the questions and every program accepted clears the module. The programs are JavaScript (the judge runs Node) that model the type system's own rules — inference and widening, structural assignability, utility types over schemas, narrowing — so you reason about what the compiler would do.

Before you start, make sure you can answer:

  • What const x = "a" versus let x = "a" infers; what as const does; the difference between any, unknown and never.
  • Why a class name does not matter for assignability; when excess properties are an error; what a discriminated union's tag is for.
  • What K extends keyof T and T[K] express; how Omit is built; what a mapped type is.
  • Which checks narrow; why typeof x === "object" keeps null; what x is T and asserts x is T do; when as is acceptable.
  • What strict turns on; what a .d.ts is; why tsc --noEmit must run in CI when esbuild builds; how to migrate JavaScript.

The programs are a schema builder that both validates values and prints the TypeScript type it would infer, an exhaustive state machine over discriminated-union states and events, and a declaration-file generator that infers .d.ts text from runtime values.

Common questions

What is the difference between any, unknown and never?

any accepts every value and disables checking wherever it is used; unknown accepts every value but must be narrowed before use; never has no values at all and marks functions that never return and unions that have been exhausted.

Why does a class name not matter for assignability in TypeScript?

TypeScript is structurally typed: assignability depends on the shape — the properties a value has — not on the name it was declared with. Two classes with the same public members are interchangeable unless they have private members.

Why must tsc --noEmit run in CI when esbuild builds the code?

esbuild strips TypeScript types without checking them, so a build can succeed with type errors in it. A separate tsc --noEmit step in CI is what actually type-checks the project and stops those errors shipping.

Exercises

A schema builder that validates and infers

Line 1 is a schema DSL: space-separated key:type fields where a trailing ? on the key means optional and a type is string, number, boolean, a T[] array, or a union of string literals "admin"|"user". Compile it, print type = { key: type; key?: type; … } in TypeScript syntax (a literal union in an array is parenthesised), then parse each following JSON line collecting every problem: <key>: required, <path>: expected <type>, got <string|number|boolean|null|array>, <path>: expected <"a" | "b">, got <JSON>, with array items addressed as tags[1]; a non-object root gives <root>: expected an object. Print ok <JSON> or error <problems joined by " | ">.

Example: name:string age:number email?:string tags:string[] role:"admin"|"user" →

type = { name: string; age: number; email?: string; tags: string[]; role: "admin" | "user" }
ok {"name":"Ada","age":36,"tags":["x"],"role":"admin"}
error age: expected number, got string | tags[1]: expected string, got number | role: expected "admin" | "user", got "guest"
error name: required
error <root>: expected an object

An exhaustive state machine

States: { status: "idle" } | { status: "loading", attempt } | { status: "success", data } | { status: "failure", error, attempt }. Events: FETCH, RESOLVE <data>, REJECT <error>, RETRY, RESET. Write transition(state, event) switching on event.type: FETCH from idle → loading(1); RESOLVE from loading → success; REJECT from loading → failure (keeping the attempt); RETRY from failure → loading(attempt + 1); RESET → idle from anywhere; any other combination returns the same state object; an unknown event type reaches assertNever (unhandled: <JSON of the event>). Print <TYPE>: <before> -> <after> with (ignored) when the state object did not change, or <TYPE>: <error message>; describe states as idle, loading(attempt n), success(<JSON data>), failure(<error>, attempt n).

Example: FETCH, FETCH, REJECT timeout, RETRY, RESOLVE {"items":3}, RESET, EXPLODE →

FETCH: idle -> loading(attempt 1)
FETCH: loading(attempt 1) -> loading(attempt 1) (ignored)
REJECT: loading(attempt 1) -> failure(timeout, attempt 1)
RETRY: failure(timeout, attempt 1) -> loading(attempt 2)
RESOLVE: loading(attempt 2) -> success("{\"items\":3}")
RESET: success("{\"items\":3}") -> idle
EXPLODE: unhandled: {"type":"EXPLODE"}

Generate a declaration file

The input is a JSON object describing a module's exports: a primitive, array or plain-object value is a constant; {"fn": <arity>} is a function; {"class": [fields], "methods": [names]} is a class. Infer types the way a declaration emitter would — string, number, boolean, null, arrays as T[] or (A | B)[] in first-seen order (unknown[] when empty), objects as { k: T; … } — and print one declaration per export in key order: export declare const NAME: T;, export declare function f(arg0: unknown, arg1: unknown): unknown;, and multi-line export declare class C { with field: unknown; and method(): unknown; lines and } (an empty class prints {} on one line).

Example (excerpt):

export declare const VERSION: string;
export declare const flags: { debug: boolean; level: string };
export declare const mixed: (number | string | null)[];
export declare function add(arg0: unknown, arg1: unknown): unknown;
export declare class Point {
  x: unknown;
  y: unknown;
  dist(): unknown;
  toString(): unknown;
}
export declare class Empty {}

In this module: TypeScript preview

← TypeScript in practice — tsconfig, declaration files, builds, migration and the errors you will see · ES2020–ES2022 — the features that changed everyday code →