From JavaScript to TypeScript: Project Setup, Compiler and a Safe First Migration
Learn what the TypeScript compiler does, configure tsconfig, move a JavaScript file to TypeScript and separate type-level ideas from runtime code.

1. Start with JavaScript that already works
export function fullName(user) {
return `${user.firstName} ${user.lastName}`;
}
export function ageNextYear(user) {
return user.age + 1;
}2. Add TypeScript locally
npm install --save-dev typescript
npx tsc --init3. Understand tsc versus Babel
| Tool | Primary job | Type-checks TypeScript? |
|---|---|---|
| tsc | Type checking + JavaScript emit | Yes |
| Babel | Syntax transformation | Not by itself |
| ESLint | Static lint rules | Not a TypeScript compiler |
Some build tools can strip TypeScript syntax without doing a full type check. Keep a dedicated type-check command in CI when your bundler is not using tsc for that responsibility.
4. Rename one file, then type the inputs
type User = {
firstName: string;
lastName: string;
age: number;
};
export function fullName(user: User) {
return `${user.firstName} ${user.lastName}`;
}
export function ageNextYear(user: User) {
return user.age + 1;
}5. Turn on useful strictness
{
"compilerOptions": {
"strict": true,
"noEmit": true,
"target": "ES2022",
"module": "ESNext"
}
}6. Replace any with a real decision
| Situation | Better type |
|---|---|
| You know the exact object shape | interface or type |
| Several allowed primitives | union |
| Truly unknown external value | unknown |
| Generic reusable container | generic type parameter |
| You are temporarily bypassing checks | any — but document and remove it |
7. Runtime data needs runtime checks
function isUser(value: unknown): value is { name: string; age: number } {
if (!value || typeof value !== 'object') return false;
const item = value as Record<string, unknown>;
return typeof item.name === 'string' && typeof item.age === 'number';
}
const data: unknown = await response.json();
if (!isUser(data)) throw new Error('Invalid user response');8. Migrate boundaries before internals
- 1API models
Type request and response shapes first.
- 2Shared domain objects
Users, products, payments, exam records.
- 3Utility functions
Small functions are easy to migrate and test.
- 4UI components
Move props and state once shared data types are stable.
- 5Strictness cleanup
Reduce casts and temporary any types after the app compiles.
9. Let JavaScript and TypeScript coexist when needed
TypeScript can support gradual adoption. In large systems, a mixed period can be healthier than a big-bang rewrite. Use allowJs/checkJs deliberately if it fits your build, and keep the migration plan visible.
10. Migration checklist
- ✓npm run typecheck exists and runs in CI.
- ✓External API data is not trusted only because an interface exists.
- ✓No new any types are added without a reason.
- ✓Compiler target/module settings match the runtime/build tool.
- ✓The application is still tested after each migration slice.
- ✓Source maps and production monitoring still work.
Official TypeScript resources
Sources and further reading
Clarvo articles are independently written. These references let you inspect the original inspiration and primary or official sources used to deepen the guide.
