TypeScript / Basic / Lesson 1
The checker, emitter, and JavaScript runtime
Separate source checking from JavaScript execution, pin a reproducible compiler baseline, and identify which assumptions belong to the runtime.
On this page
Lesson goals
- Explain which TypeScript constructs disappear during emission.
- Distinguish compiler target, library declarations, and runtime support.
- Compile and run complete examples with explicit options.
Checking and execution answer different questions
TypeScript checks a program using a static model of JavaScript values and can emit JavaScript for execution. Type annotations and interfaces are normally erased; a runtime cannot inspect an interface merely because it appeared in source. The checker can catch many inconsistent uses before execution, but it does not validate a network response, enforce authorization, or prove the absence of every runtime exception. JavaScript semantics still determine arithmetic, mutation, and property access. Keep the checker result and the observed runtime result as separate evidence when diagnosing a program.
State a compiler and environment baseline
This course verifies examples with the official TypeScript 5.7.3 compiler and Node.js 22.23.3, emitting ES2022 CommonJS for isolated single-file demonstrations. It is a stated baseline, not a claim to use the latest compiler. Checks enable strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes, and noEmitOnError. The two extra property flags are explicit because strict alone does not enable every desirable check. Standard ES2022 and DOM declarations provide names such as console during checking; declarations do not install browser APIs in Node. Runnable programs use only runtime facilities actually present in this environment.
Observe a typed calculation
The first example is a complete TypeScript module because export {} prevents accidental sharing of global script declarations. A parameter annotation describes the accepted input, and the function returns a number. After emission the annotations are gone, while multiplication and console output remain ordinary JavaScript. These small operands avoid numeric precision concerns. Save each example independently and run the emitted file only after checking succeeds. A language-service hint in an editor does not establish that the terminal used the same compiler version, project configuration, or source revision.
export {};
function double(value: number): number { return value * 2; }
console.log(double(7));
14
The checked numeric call prints fourteen. No runtime type object for number is generated by the annotation.
Decide whether errors can produce artifacts
The compiler can be configured to emit even when errors exist, so use noEmitOnError when successful checking is required before output. noEmit performs checking without generating files, useful when a separate build tool handles transformation. target selects emitted language syntax, and lib selects declarations visible to checking; neither supplies every missing runtime capability. A project should record its commands and local compiler dependency. For an isolated file, explicit command-line options are reviewable; for a multi-file application, a checked tsconfig and the correct project invocation reduce accidental differences.
Inspect the JavaScript value that remains
The second example uses an interface for checking an object literal, then reads the same ordinary object at runtime. Its output depends on the actual fields, not on the interface existing after compilation. If untrusted JSON claimed a different shape, the annotation would not repair those bytes. Later lessons introduce unknown and explicit validation at that boundary. Reproduce a problem with the compiler command, diagnostic, emitted JavaScript, and runtime command together. Do not fix an execution failure by merely weakening types until the checker becomes quiet.
export {};
interface Item { label: string; count: number }
const item: Item = { label: "cards", count: 3 };
console.log(`${item.count} ${item.label}`);
3 cards
The object has two actual properties. The interface is a compile-time description and supplies no runtime validation.
Common errors and fixes
A checked program fails on an unavailable API.
- Why it happens
- Library declarations were mistaken for a runtime implementation.
- How to fix it
- Use a supported runtime facility or provide the documented implementation.
- Prevent it next time
- Verify target, lib, and deployment runtime separately.
An old emitted file runs after a failed check.
- Why it happens
- Build and run commands did not guard artifact freshness.
- How to fix it
- Require successful checking and identify the exact output file.
- Prevent it next time
- Keep isolated output directories and reproducible commands.
An interface is expected to validate parsed JSON.
- Why it happens
- Erased types were treated as runtime guards.
- How to fix it
- Validate an unknown value before domain use.
- Prevent it next time
- Document every untrusted input boundary.
Check your understanding
1. Change the first call to double(9). State the emitted program result and identify which information is erased.
Show worked solution
The result is eighteen followed by a newline. Parameter and return annotations disappear during emission, while the function and multiplication remain JavaScript. The checker verifies the source call uses a number; execution computes from the supplied value. Rebuild before comparing output so a stale file does not conceal the change.
2. A project includes DOM declarations and calls document.querySelector in Node. Explain why checking can succeed and propose an appropriate investigation.
Show worked solution
The declarations describe a browser global but do not create document in Node. Confirm the intended runtime and inspect whether this code belongs in a browser entry point. Remove incompatible assumptions or supply a deliberate supported environment; changing an annotation or adding an assertion cannot create the missing object. Test the actual emitted entry point.
Key takeaways
- Separate checking, emission, and execution.
- Pin configuration and verify the runtime independently.
- Types describe data; validation examines actual values.