Emmanuel Akinfulubi

Glossary

61 terms, each with the sentence you would say out loud and the fuller version you want when something is not behaving. Every entry names the lesson that covers it.

Several of these exist specifically to mark a distinction people blur: private against #, an optional property against one that may hold undefined, the two meanings of typeof, structural against nominal typing. Being able to name those precisely is most of what separates confident TypeScript from hopeful TypeScript.

61 terms

Ambient declaration

A declare statement describing something that exists elsewhere.

Tells the compiler about a value or type without emitting anything for it. The contents of a .d.ts file are all ambient, which is how you describe JavaScript that was never written in TypeScript.

Lesson 15

Annotation

The : Type you write after a name.

A claim about what a value is allowed to be, checked at compile time and deleted before run time. Annotate the boundaries — parameters, exported signatures, empty containers — and let the interior infer.

Lesson 02

any

The type that switches checking off.

Accepts every value and permits every operation, so no mistake involving it is reported. It also spreads, because a property of any is any. Defensible when contained inside a function body and commented; a problem in a signature, where it becomes every caller's.

Lesson 12

Assertion

An as expression, telling the compiler to believe you.

Performs no check and emits no code. The compiler refuses assertions between wholly unrelated types, which people route around with as unknown as T — at which point the last guard is gone and a comment explaining what you know is the minimum.

Lesson 13

Assertion function

A function returning asserts value is T, which throws rather than returning a boolean.

Narrows everything after the call, so the happy path stays flat with no nesting. Must be declared with an explicit type annotation; an inferred arrow function will not work.

Lesson 13

Awaited

The built-in type that unwraps a promise.

Awaited<Promise<string>> is string, and it recurses so nested promises unwrap all the way down. Usually combined with ReturnType to get what an async function resolves to.

Lesson 16

Bivariance

Accepting a parameter type whether it is wider or narrower than required.

Only one of those directions is safe. strictFunctionTypes fixes it for function type positions but deliberately not for method syntax, because the standard library depends on the looser rule. So handle(a: A): void and handle: (a: A) => void are checked differently.

Lesson 06

Conditional type

A ternary that operates on types: A extends B ? X : Y.

Here extends means “is assignable to”, not “inherits from”. Applied to a union it distributes over the members automatically, which is how Exclude and Extract work and also the cause of most surprising results.

Lesson 17

Contextual typing

A parameter typed from where it is used rather than from what it receives.

Why words.map((w) => w.toUpperCase()) needs no annotation on w. It also explains a common refactoring failure: extract that callback into a named const and the context disappears, so noImplicitAny objects.

Lesson 02

Contravariance

Parameters being checked in the opposite direction from return types.

A function is substitutable if it accepts at least what the contract promises to pass. So a handler taking a wider parameter is safe, and one taking a narrower parameter is not — which is what strictFunctionTypes enforces.

Lesson 06

Declaration file

A .d.ts file: types with no implementation.

Describes the shape of JavaScript. The compiler generates them for you with declaration: true, which is how a package ships types for its compiled output. You write one by hand to describe an untyped dependency, declare a global, or teach the compiler about a non-code import.

Lesson 15

Declaration merging

Two interfaces with the same name combining rather than clashing.

Indispensable for adding a property to a type you do not own — a framework's request object, say. A hazard inside your own code, because a duplicated name silently combines instead of erroring. One reason to prefer type for shapes you control.

Lesson 03

Discriminant

A shared property whose literal type differs across a union's members.

The tag the compiler reads to work out which member you are holding. It must be a literal type — declaring the tag as string silently disables the whole mechanism, which is a genuinely common mistake.

Lesson 04

Discriminated union

A union whose members share a tag property.

The workhorse pattern for shapes, results and states. Checking the tag narrows to that member, and combining it with an annotated return type plus assertNever means adding a variant produces a compiler-generated list of every place you need to visit.

Lesson 04

Distribution

A conditional type running once per member of a union.

Happens automatically and is usually what you want. Wrapping both sides in tuples — [T] extends [U] — stops it. If a conditional type is producing boolean where you expected true, or collapsing to never, distribution is almost always why.

Lesson 17

enum

A named set of constants, and the one feature that emits code you did not write.

A numeric enum produces a double mapping so Object.keys returns twice as many entries as it has members. A string enum is nominal, so a plain matching string is not assignable to it. Node cannot run a file containing one. Prefer as const plus a derived union.

Lesson 14

Erasure

Types being deleted before the code runs.

The fact that explains most of TypeScript's behaviour. Nothing type-shaped exists at run time, which is why instanceof works on a class and cannot work on an interface, and why generics cannot be inspected. The exceptions — enum, parameter properties, namespace — are the constructs Node refuses to strip.

Lesson 01

exactOptionalPropertyTypes

The flag distinguishing “absent” from “present and undefined”.

With it on, x?: string means the key may be missing but may not hold undefined. The distinction is observable at run time through in and Object.keys, and it matters for anything that serialises. Object spreads are where it bites.

Lesson 08

Excess property checking

The rule rejecting unknown properties on a fresh object literal.

A pragmatic typo-catcher rather than a consequence of the type system, which is why it looks inconsistent: a literal at the assignment position is checked, and the same object through a variable is not. Knowing it applies only to literals is enough to stop being surprised.

Lesson 03

Exhaustiveness check

Proving every member of a union has been handled.

Achieved by annotating the return type, or by passing the value to a function taking never in the default branch. Both turn a new variant into a compile error rather than a silent fall-through. Record<Union, V> gives you the same guarantee for objects.

Lesson 04

Generic

A type filled in by the caller.

Exists to express a relationship — between two parameters, or between a parameter and the return type. A type parameter appearing only once expresses no relationship and should be a plain parameter type or unknown instead.

Lesson 09

implements

A class declaring that it satisfies an interface.

Adds no behaviour and only asks the compiler to check. Technically optional, because typing is structural — but it puts the error on the class where the mistake is, rather than at every call site.

Lesson 11

Index signature

[key: string]: T, for objects whose keys are not known in advance.

Lets you read any string key. By default it lies, reporting T for a key that is absent; noUncheckedIndexedAccess makes it T | undefined, which is the truth. Record<K, V> is the readable spelling.

Lesson 03

Indexed access type

T[K]: the type of a property, looked up rather than repeated.

Composes freely: User['address']['city'] reaches two levels down, and T[number] gives an array or tuple its element type. The basis of the as const union pattern and of the get<T, K> signature.

Lesson 10

infer

A type variable declared inside a conditional type, bound to whatever matched.

Pattern matching on types. T extends (...args: never[]) => infer R ? R : never is how ReturnType is implemented. Prefer never[] to any[] in a constraint you only want to match against.

Lesson 17

Inference

The compiler working out a type you did not write.

Covers the vast majority of real code. const infers the exact literal value while let widens to the general type, and that single difference is behind a whole family of confusing errors.

Lesson 02

interface

A named description of an object shape.

Interchangeable with a type alias for plain object shapes. Differences that matter: an interface can be re-declared and merged, which you need for augmenting other people's types; a type can express unions, tuples and computed types, which an interface cannot.

Lesson 03

Key remapping

The as clause in a mapped type, which rewrites key names.

[K in keyof T as `get${Capitalize<K>}`] derives one shape's keys from another's. Mapping a key to never deletes the property, which is the mechanism behind every “pick the properties whose type is X” helper.

Lesson 17

keyof

The union of a type's keys.

Derived rather than written out, so it updates itself when the type gains a property. Combined with a constrained type parameter it produces the get<T, K extends keyof T>(o: T, k: K): T[K] signature, which is the shape worth knowing by heart.

Lesson 10

lib

The compiler option deciding which built-ins exist.

Controls what the compiler believes is available — document needs "dom", Array.prototype.at needs a recent ECMAScript entry. It ships no code, so a lib ahead of your runtime converts a compile-time error into a runtime one.

Lesson 15

Literal type

A type containing exactly one value, such as 'north'.

Nearly useless alone and the most valuable thing in the language in a union. Replacing a string parameter with a union of literals gives you autocomplete, typo checking and exhaustiveness for one line of effort.

Lesson 04

Mapped type

A type that walks the keys of another to build a new one.

{ [K in keyof T]: ... }. readonly and ? can be added, and -readonly or -? remove them. This is how Partial, Readonly, Required and Pick are actually implemented.

Lesson 17

moduleResolution

The option deciding how import paths are resolved.

"bundler" makes extensions optional and is right when something processes your code. "nodenext" requires them, and you write ./types.js to reach types.ts. Imports that resolve in your editor and fail at run time are nearly always this.

Lesson 15

namespace

TypeScript's pre-modules scoping construct.

Emits a real runtime object, like enum. Kept for backwards compatibility and for declaration files describing global scripts. Do not write new ones — ES modules do the job, and file boundaries give you the scoping for free.

Lesson 15

never

The type with no values in it.

What a function that always throws returns, and what a fully-handled union collapses to — which is why assertNever works. It also disappears from a union, and that disappearance is the mechanism behind type-level filtering.

Lesson 12

noImplicitAny

The flag requiring an annotation where nothing can be inferred.

Part of strict. The reason plain JavaScript is not, in practice, valid TypeScript: an un-annotated parameter has nothing to infer from and is rejected rather than silently becoming any.

Lesson 01

Nominal typing

A type matching by name rather than by shape. TypeScript does not do this.

The idea to unlearn if you came from Java or C#. TypeScript is structural. Where you genuinely need nominal behaviour, a #private field achieves it, because no object literal can replicate one.

Lesson 03

Non-null assertion

The ! that claims a value is not null or undefined.

Erased, so it provides no runtime protection at all — it relocates the failure rather than preventing it. Reasonable when you have information the compiler cannot, such as bounds you just proved; otherwise a real check is the answer.

Lesson 05

noUncheckedIndexedAccess

The flag making arr[i] return T | undefined.

Not part of strict, and worth turning on. Iteration is unaffected, so preferring for...of and map avoids most of the friction — and the friction that remains is exactly where the risk is real. Tuples are exempt, because their length is known.

Lesson 07

Nullish coalescing

??, which substitutes only for null and undefined.

The important contrast is with ||, which also discards 0, '' and false. If those are legitimate values, || silently treats them as missing, and the resulting bugs are subtle and slow to find.

Lesson 08

Optional chaining

?., which short-circuits at a missing link.

Guards exactly the one link it is attached to and nothing else. Sprinkling it along a whole path to make errors disappear hides real problems — put it where a value is legitimately optional.

Lesson 08

Optional property

A property marked ?, which may be absent.

Its type includes undefined, so absence has to be handled. Distinct from a required property whose type includes undefined: the first may be missing entirely, the second must be present. exactOptionalPropertyTypes makes the difference enforceable.

Lesson 03

Overload

Several call signatures backed by one implementation.

For when the return type genuinely depends on the value of an argument. Verbose, easy to get subtly wrong, and the compiler picks the first matching signature rather than the best one. Try a union, an optional parameter or a generic first.

Lesson 06

Parameter property

constructor(public name: string), which declares and assigns a field.

One of the few TypeScript constructs that generates code rather than being erased, which is exactly why node file.ts refuses it. Declare fields explicitly if you want your files runnable directly.

Lesson 11

Partial

The utility type making every property optional.

The shape of almost every update payload. Shallow: it makes the top-level properties optional and leaves nested objects exactly as they were, which surprises people once.

Lesson 16

Predicate

A function returning value is T, which teaches the compiler a check.

Narrows in both branches, exactly like a built-in check. The compiler does not verify that the body proves the claim, so a predicate is where you take responsibility — keep it short and test it.

Lesson 13

private

The TypeScript keyword restricting access at compile time only.

Checked and then erased, so anything reaching the object without type checking can read the field. Contrast #, which is real JavaScript and genuinely unreachable. Use # when the privacy matters; use private when you need subclass access, since there is no #protected.

Lesson 11

Readonly

A modifier or utility type preventing assignment.

Checked at compile time and gone at run time, so it is a promise between parts of your own typed code rather than a lock. Object.freeze is the runtime equivalent. On a parameter it is a cheap, checked guarantee that you will not modify the caller's data.

Lesson 03

Record

Record<K, V>: an object type built from a key type and a value type.

Over a union of literals it demands an entry for every member, which gives you an exhaustiveness check on an object for free. Add a member to the union and every Record over it becomes an error listing what is missing.

Lesson 16

satisfies

Checks a value against a type without replacing its inferred type.

An annotation checks and then widens to what you wrote, losing detail. satisfies checks and keeps the precise inferred type, so the compiler still knows exactly which keys a literal has.

Lesson 02

strict

The single option turning on eight separate checks.

The most consequential line in a tsconfig, and the correct value is true. Never disable one of its members to silence an error — suppress per line with @ts-expect-error, which cleans itself up when you fix the cause.

Lesson 18

strictNullChecks

The flag making null and undefined separate types.

Catches more real bugs than everything else in the language combined, because “I did not expect that to be missing” is the most common runtime failure in JavaScript. Without it, every type silently includes both.

Lesson 08

Structural typing

A value fitting a type if it has the right shape.

Nothing else is consulted — not the name, not where the value came from, not what it was declared as. An object with extra properties satisfies a smaller type, which is the same reasoning as subtyping arrived at by shape rather than declaration.

Lesson 03

Template literal type

A type built from a string pattern, such as `/${Resource}`.

Computes the cross product of the unions inside it, so a malformed route or a wrong CSS unit becomes a compile error. Combines with Capitalize and friends to derive names mechanically.

Lesson 17

Tuple

An array type with a known length and a type per position.

Exempt from noUncheckedIndexedAccess, because the compiler knows the length, so indexing stays clean. Comfortable at two or three elements; beyond that positions stop being memorable and an object is kinder.

Lesson 07

Type alias

A name for a type, written with type.

Interchangeable with an interface for object shapes, and required for anything an interface cannot express: unions, tuples, and computed types. Unlike an interface it cannot be re-declared, which is a feature inside your own code.

Lesson 03

Type parameter

The placeholder in angle brackets, conventionally T.

An ordinary parameter that holds a type instead of a value. It has a scope, it can have a default, and it can be constrained with extends. Use a real name when it helps — <Item> beats <T> in anything non-trivial.

Lesson 09

typeof (type position)

A query asking the compiler what type a value has.

Unfortunate reuse of a keyword: in a value position typeof is the JavaScript operator producing a string at run time, and in a type position it is a compile-time query. Which you get depends entirely on where you wrote it.

Lesson 10

unknown

The type that accepts anything and permits nothing.

The honest type for every unverified value entering your program — JSON.parse, a fetch result, a caught error. You get out of it by narrowing, which is exactly the check that should have been happening anyway.

Lesson 12

verbatimModuleSyntax

The flag requiring explicit import type.

Makes what you wrote exactly what is emitted, rather than relying on the compiler to work out which imports to erase. Worth turning on: it also makes type-only import cycles obvious, and those are the cycles that do not exist at run time.

Lesson 15

Widening

An inferred literal type becoming its general type.

Why let heading = 'north' is string while const heading = 'north' is the literal 'north'. The fix when you needed the precision is const, an explicit union annotation, or as const — in that order of preference.

Lesson 02

Newsletter

New writing, when it lands.

Occasional notes on shipping software with AI agents. No schedule, no filler, unsubscribe in one click.