Best Practices & Next Steps
Pull together everything you've learned with real-world TypeScript best practices: strict mode, project setup, third-party types, linting, and migration.
On this page 12 sections
Introduction
You've now covered TypeScript's core type system — from basic types through generics, enums, and narrowing. This final lesson focuses on the practical, real-world side: how to configure a project well, work with the wider ecosystem, and where to go from here.
- How to configure and roll out strict mode effectively.
- How to structure a TypeScript project's tsconfig.json for real work.
- How to get types for third-party JavaScript libraries.
- How linting and formatting tools fit alongside the compiler.
- How to migrate an existing JavaScript codebase to TypeScript gradually.
Configuring Strict Mode
The single "strict": true flag in tsconfig.json actually bundles together a whole family of checks: strictNullChecks (null and undefined must be handled explicitly), noImplicitAny (every value must have a known type), strictFunctionTypes, and several others. Turning strict mode on catches dramatically more real bugs, and it is far easier to start a project strict than to retrofit it onto a large, already-loose codebase later.
Project Structure and tsconfig
A well-organized tsconfig.json typically separates source from compiled output using rootDir and outDir, limits compilation scope with include/exclude, and can define path aliases so imports like @/components/Button resolve cleanly instead of long relative paths like ../../../components/Button.
{ "compilerOptions": { "strict": true, "target": "ES2020", "rootDir": "src", "outDir": "dist", "baseUrl": ".", "paths": { "@/*": ["src/*"] } }, "include": ["src"], "exclude": ["node_modules", "dist"]}Working with Third-Party Types
Many npm packages ship their own type declarations (.d.ts files) built in, so importing them just works with full autocomplete. For older or plain-JavaScript-only packages, the community-maintained DefinitelyTyped project publishes matching type packages under the @types/ scope.
# For a library without built-in typesnpm install --save-dev @types/lodashLinting and Formatting
The TypeScript compiler checks types, but doesn't enforce style or catch every code-quality issue. ESLint with the @typescript-eslint plugin adds type-aware linting rules (like flagging unused variables or unsafe any usage), while Prettier handles automatic code formatting — the two are commonly used together, running alongside tsc in CI.
TypeScript with Frameworks
React components are typically written in .tsx files, typing props with an interface. Next.js has TypeScript support built in with zero configuration. Node.js backends add @types/node (and @types/express for Express) for full typings of the runtime and framework APIs. Angular is TypeScript-first by design, and modern runtimes like Deno and Bun understand TypeScript natively, with no separate compile step required during development.
interface ButtonProps { label: string; onClick: () => void;}
function Button({ label, onClick }: ButtonProps) { return <button onClick={onClick}>{label}</button>;}Migrating JavaScript to TypeScript
You do not need to convert an entire codebase at once. The standard approach is incremental: enable allowJs and checkJs so TypeScript can type-check plain .js files using JSDoc comments, then rename files from .js to .ts one at a time, starting with the most-used, most-stable modules. Many teams start with a looser tsconfig on legacy code, then tighten strict settings gradually as more of the codebase is converted.
Common Mistakes
- Enabling every strict flag on a large legacy codebase all at once, producing thousands of errors instead of migrating incrementally.
- Reaching for
@ts-ignoreto silence a compiler error instead of understanding and fixing the actual underlying issue. - Skipping
@types/*packages for third-party JavaScript libraries, quietly losing type safety at every call into that library. - Never actually running
tsc --noEmitin CI, treating TypeScript as documentation rather than an enforced, automated check.
Best Practices
- Run
tsc --noEmitin CI so type errors block a merge, exactly like a failing test would. - Keep
@types/*packages updated alongside the libraries they describe. - Adopt strict mode as early as possible — even mid-migration, new files can be strict while older files are grandfathered in.
- Treat compiler complaints as a collaborator catching a real bug, not an obstacle to silence.
Frequently Asked Questions
Practice TypeScript inside a real framework — React with .tsx, Next.js, or Node with Express — and get comfortable with the utility types (Partial, Pick, Omit, Record) covered in the generics lesson.
No — if a library ships its own .d.ts files (increasingly the norm), you get full types automatically with no extra install.
Yes — that is the standard, recommended approach: enable allowJs, rename files incrementally, and tighten strict checks over time rather than converting everything at once.
Yes — most professional frontend and Node.js backend roles today expect TypeScript, and it catches an enormous class of real bugs before they ever reach production.
Key Takeaways
"strict": truebundles the checks that catch the most real bugs — enable it from day one on new projects.- A well-structured tsconfig.json separates source and output and can define clean import path aliases.
- @types/* packages from DefinitelyTyped provide types for JavaScript-only libraries.
- ESLint (with @typescript-eslint) and Prettier complement, rather than replace, the TypeScript compiler.
- Large JavaScript codebases can migrate to TypeScript incrementally, file by file, without a risky rewrite.
Summary
Congratulations — you've completed the TypeScript course, from what TypeScript is and why Microsoft built it, through the full type system, object-oriented programming, generics, and now real-world project practices. The best next step is to build something real: add TypeScript to a small project, turn on strict mode, and let the compiler guide you as you go.