Command Palette

Search for a command to run...

Hectal
PHASE 17Advanced ~30 min· topic 1 of 5

Topic 17.1

Interview Questions: Fundamentals

In one line

The questions every TypeScript interview starts with (type vs interface, any vs unknown vs never, structural typing, enums vs unions, narrowing, satisfies), each answered completely and proved with a live example.

Think of it like this

A driving test. The examiner does not want you to recite the highway code; they want to see you handle a junction. A TypeScript interview is the same. Saying 'unknown is safer than any' earns a nod. Showing the one line where any lets a crash through and unknown stops it earns the job. Prove each claim with code, and this topic gives you the proof for the six most common questions.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Declaration merging
Two declarations with the same name combine into one. Interfaces merge; type aliases do not.
Top type
A type every value fits into. unknown is the safe one, any is the unchecked one.
Bottom type
A type no value has. That is never.
Structural typing
Two types are compatible when their shapes match. Names do not matter.
Fresh object literal
An object written directly at the place that expects a type, like const p: Point = { x: 1, y: 2 }. Extra properties are rejected only here.
Narrowing
The compiler making a type smaller inside an if or switch because of a check.
Discriminated union
A union whose members share a property with a different literal value in each, the tag that tells them apart.
Type predicate
A function return type like value is Order that tells the compiler what a true result proves.
satisfies
An operator that checks a value against a type but keeps the value's own, more precise type.

Step by step

01Start every answer with the one-line difference

Interviewers score the first sentence. Make it a clear contrast: 'An interface can be merged and extended, a type alias can name any type.' Then add one proof.

The first example below shows any, unknown and never side by side. any compiles and crashes. unknown is rejected until you check. never appears as the empty array element type, as string & number, and as a value that vanishes from a union.

02any vs unknown, with the crash

In withAny, value is any, so value.toFixed(1) compiles. At runtime value is the text 'hello', which has no toFixed, and the program throws value.toFixed is not a function.

In withUnknown the same line is the error TS18046: 'value' is of type 'unknown'. You must prove it is a number first, as safe does with typeof.

unknown.tswhole filets
function safe(value: unknown): string {
  return typeof value === "number" ? value.toFixed(1) : "not a number";
}

03Structural typing: what fits, and the one exception

A class Vec with x, y and label fits an interface Point that needs only x and y. TypeScript never asks whether Vec was declared to implement Point.

A fresh object literal with an extra property is rejected (TS2353), but the same object stored in a variable first passes. Say why: the check exists to catch typos, and a variable has no 'typo' moment.

The same example shows interface merging working and type alias merging failing with TS2300: Duplicate identifier.

04Enums, unions and as const

Show the modern pattern in three lines: an as const object, then type Mode = (typeof Mode)[keyof typeof Mode]. (typeof in a type position, keyof and indexed access are Topics 8.1 to 8.3.) Hover shows 'dark' | 'light'. The object exists at runtime (so Mode.Dark works), the union exists at compile time, and no enum code is emitted.

Mention what the old enum Color compiles to: a real object with both Color.Red === 0 and Color[0] === 'Red'.

05Narrowing and satisfies in one picture

After if (m === 'light') return ..., the parameter m is narrowed to 'dark'. Hover it with // ^? to prove it.

palette uses satisfies: a typo in a key or a wrong value type would be an error, yet palette.accent stays a string, so .toUpperCase() works. With a plain annotation it becomes string | number[] | undefined (try it in Break it).

06How to practise

Cover the answer, read the question, answer aloud, then reveal. Count a point for every claim you backed with a code example. Aim for one example per answer.

How to practisediagram
Rendering diagram…

Try it yourself

  1. 1

    Predict, then check

    In the first example, change the body of withUnknown to if (typeof value === "string") return value.toFixed(1); followed by return "x";. Predict the new error: the old TS18046 is gone because value is now narrowed to string, and toFixed does not exist on string. The panel shows TS2551: Property 'toFixed' does not exist on type 'string'. Did you mean 'fixed'? on that line.

  2. 2

    Move the extra property

    In the second example, add the line const direct: Point = { x: 1, y: 2, z: 3 }; just above const stored. Which line does the new TS2353 point at, and under which word? It is the same error as on the fresh line, because both are fresh object literals. Then explain why viaVariable still passes.

  3. 3

    Add a case

    In the third example, add "auto" to the Mode object (Auto: "auto"). Predict what label("auto") returns and what the hover on m shows now. The hover is "auto" | "dark" (both are left after the if removes "light"), so return m still compiles and label("auto") returns auto.

Code & diagrams

any vs unknown vs never

any compiles and crashes, unknown is rejected until you check, never is the type with no values. Run it, then change `unknown` to `any` in withUnknown and watch the error disappear.

Console

(nothing was printed)

Expected console output

value.toFixed is not a function
not a number 2.0
Structural typing, excess properties and merging

A class fits an interface by shape. The fresh literal with z is rejected, the stored one is not. Two interfaces named Config merge, two type aliases named Alias do not.

Console

(nothing was printed)

Expected console output

1 2 8080
Enums, literal unions, narrowing and satisfies

An as const object gives runtime values and a compile-time union. The parameter m narrows to dark after the if. satisfies keeps accent as a plain string.

Console

(nothing was printed)

Expected console output

dark 2 Green dark

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Swap satisfies for an annotation

In the third example, change the palette line to const palette: Record<string, string | number[]> = { primary: [255, 0, 0], accent: "#0f0" };.

terminal
$ Types panel
── what you'll see ──
index.ts(21,1): error TS18048: 'palette.primary' is possibly 'undefined'.
index.ts(21,17): error TS2339: Property 'map' does not exist on type 'string | number[]'.
Property 'map' does not exist on type 'string'.
index.ts(21,22): error TS7006: Parameter 'n' implicitly has an 'any' type.
index.ts(22,1): error TS18048: 'palette.accent' is possibly 'undefined'.
index.ts(22,16): error TS2339: Property 'toUpperCase' does not exist on type 'string | number[]'.
Property 'toUpperCase' does not exist on type 'number[]'.

Myth vs fact

Myth

interface is faster and type is slower, so always use interface.

Fact

Interfaces can be cached by name, which sometimes helps checker speed on very large projects, but the difference is rarely visible. Choose for what you need (merging and extending, or unions and mapped types), not for speed.

Myth

unknown is just a stricter any.

Fact

They sit at the same place in the hierarchy (every value fits in both) but behave in opposite ways: you can do anything with any and nothing with unknown until you narrow it.

Myth

Enums are bad and must never be used.

Fact

They work and are fine in code that already uses them. The case against them is that they emit JavaScript, numeric ones are loose, and they clash with type-stripping tools (erasableSyntaxOnly). A union or an as const object avoids all three.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    any is both the top and the bottom of the assignability lattice: it is assignable to and from nearly everything. That is why one any can quietly spread through a program (Topic 16.5). unknown is only a top type; never is only a bottom type.

  • ▸

    Excess property checks only apply to fresh object literals, and weak types (all-optional shapes) also reject objects that share no property with them. Both are heuristics for catching typos, not part of assignability itself (Topic 16.1).

  • ▸

    Numeric enums were unsound for years: any number was assignable. Since TypeScript 5.0 all enums are union enums and out-of-range literals are errors, but a number variable is still assignable to a numeric enum.

  • ▸

    satisfies also changes contextual typing: a function in an object checked with satisfies gets its parameter types from the target type, which an as cast never does.

Remember this

  1. 1

    The problem this solves: TypeScript interviews keep returning to the same few questions, and a memorised definition is easy to spot. Here each answer is a one-line difference plus a proof you can run, so you can explain it instead of reciting it. Type vs interface (Topic 3.4). Both describe the shape of an object, and for plain objects they are almost interchangeable. The real differences are small and testable: an interface can be reopened (declaration merging: two interface Config blocks join into one), while a type alias with the same name twice is an error. Only a type can name a union (A | B), a tuple (a fixed-length array, Topic 5.2), a primitive or a mapped type (Topic 8.4). interface can be extended with extends, and type combines shapes with &. A good default: interface for object shapes that other code may extend, type for everything else. Say that a team rule beats either choice.

  2. 2

    any vs unknown vs never. any switches checking off: you can call anything on it, assign it anywhere, and the mistake appears at runtime. unknown is the safe top type: any value fits into it, but you can do nothing with it until you narrow it with typeof, instanceof or a type guard. never is the empty type: no value has it. It is the return type of a function that always throws, the type left over after all cases are handled, and the reason a switch can be checked for completeness. In one line: any = give up, unknown = check first, never = impossible.

  3. 3

    Structural typing. TypeScript compares shapes, not names. If an object has every member a type requires, it fits, even if it came from a different class or was never declared as that type. That is why a Vec class with x and y can be passed where a Point interface is expected. The one place it is stricter: a fresh object literal written directly where a type is expected is checked for excess properties, because an extra property there is usually a typo. The same object stored in a variable first passes.

  4. 4

    Enums vs literal unions. A string literal union ('light' | 'dark') is erased completely: no JavaScript is emitted, values are plain strings, and it works with erasableSyntaxOnly. An enum is real JavaScript code (an object, with reverse mappings for numeric enums) and numeric enums accept any number in older versions of the checker. The modern pattern is an as const object plus a derived union type, which gives you named values at runtime and a union at compile time, with no extra emit. Use an enum only when you must match existing code that uses one.

  5. 5

    Narrowing. The compiler follows your code's control flow and shrinks a union as checks happen. The tools: typeof x === 'string', instanceof, the in operator, equality (=== undefined), truthiness, Array.isArray, a discriminant property on a discriminated union (shape.kind === 'circle'), and your own type predicates (value is Order). Be ready to name the pitfall: truthiness narrowing drops 0 and the empty string too.

  6. 6

    **satisfies vs annotation vs as.** An annotation (const x: T = v) checks v and then forgets the precise type: x is just T. as overrides the checker without proving anything. satisfies T (TypeScript 4.9) checks v against T and keeps the precise type of v. Use satisfies for config objects where you want errors on typos and still want palette.accent to be a string, not a union.

  7. 7

    Interviewers also love follow-ups: 'what is type erasure?', 'how do you type JSON.parse?', 'what does strict mode turn on?'. The answers below cover them. Practise answering aloud in under a minute each, with one small code example.

  8. 8

    Recall, so you know where to revisit each answer: any and unknown are Topic 1.6, never is Topics 2.3 and 5.5, structural typing is Topic 3.5, enums are Topic 5.3, narrowing is Topics 4.3 to 4.7, and satisfies and as are Topic 5.6. Every explain answer below ends with its topic number.

Explain it without notes

01

What is the difference between type and interface, and which do you use?

02

Explain any, unknown and never. When do you reach for each?

03

What is structural typing, and what is an excess property check?

04

Should I use an enum or a union of string literals?

05

How does narrowing work? Name five ways to narrow a type.

06

What does satisfies do, and how is it different from as and from a type annotation?

07

What is type erasure, and what does it mean for how you handle data from outside?

08

How would you type the result of JSON.parse or a fetch response safely?

Practice

01

Make this function safe. Right now it compiles but crashes for some input. Use unknown and narrowing. ``ts function shout(input: any): string { return input.toUpperCase() + "!"; } console.log(shout("hi")); console.log(shout(42));

02

Replace this enum with a union that has the same safety and no emitted JavaScript, and keep a runtime list of the values. ``ts enum Role { Admin = "admin", Editor = "editor", Viewer = "viewer", }

03

Write one object routes with satisfies so a typo in a value is an error but routes.home is still the exact string "/". Prove it with // ^?.

Trade-offs

  • ↔

    A short, confident answer with one code proof beats a long definition. But do not skip the nuance: say when the 'rule' has exceptions (enums in legacy code, any at an untyped boundary with a comment).

  • ↔

    Interviewers sometimes ask for a rule ('always interface'). Give the default and the reason, then name the case that breaks it. That shows judgement, which is what they are scoring.

Done when you can

  • I can state the difference between type and interface in one sentence and show declaration merging.

  • I can explain any, unknown and never with one example each.

  • I can explain excess property checks and why a stored object passes.

  • I can replace an enum with a union or an as const object.

  • I can name five narrowing tools and one narrowing pitfall.

  • I can explain satisfies against an annotation and against as.