TypeScript is JavaScript with a static type system added on top. Every JavaScript program is valid TypeScript syntax; TypeScript adds type annotations, a compiler that checks them before the code runs, and a build step that removes them again. At runtime there is only JavaScript, so the difference is entirely in what you find out before you ship.
In JavaScript the same function is the code minus type Product = ..., : Product[] and : number. The types add information for the compiler and the editor; they do not change what the program does.
TypeScript vs JavaScript at a Glance
| JavaScript | TypeScript | |
|---|---|---|
| Type system | Dynamic: types belong to values and are only known at runtime | Static: types are declared or inferred and checked at compile time |
| When type errors show up | When the line runs (undefined, NaN, TypeError) | In the editor as you type, and when you compile |
| Runs in | Browsers, Node.js, Deno, Bun, directly | The same places, after the types are removed |
| Build step | None needed | tsc or a bundler, or a runtime that strips types itself |
| Files | .js, .mjs, .cjs | .ts, .mts, .cts, .tsx, plus .d.ts type declarations |
| Runtime speed | Baseline | Identical: the output is JavaScript |
| Editor support | Autocompletion from inferred types and library typings, which can be incomplete | Autocompletion, rename and "find all references" from declared types |
| Learning curve | Lower | JavaScript plus the type system |
| Standard | ECMAScript, by TC39 | A Microsoft open source project that tracks ECMAScript |
The Same Code in Both Languages
Here is a function in JavaScript. Nothing in it says what user must look like:
function greeting(user) {
return `Hello, ${user.firstName} ${user.lastName}`;
}
greeting({ firstname: "Ada", lastName: "Lovelace" });
// "Hello, undefined Lovelace", no error anywhere
The TypeScript version states the shape once, and the typo is reported before the code runs:
interface User {
firstName: string;
lastName: string;
}
function greeting(user: User): string {
return `Hello, ${user.firstName} ${user.lastName}`;
}
greeting({ firstname: "Ada", lastName: "Lovelace" });
// error TS2561: Object literal may only specify known properties,
// but 'firstname' does not exist in type 'User'. Did you mean to write 'firstName'?
The annotations are the whole difference in syntax. TypeScript also adds a few declarations of its own (interface, type, enum, generics like Array<string>, access modifiers like private), but the statements, operators and built-in objects are JavaScript's.
What TypeScript Catches That JavaScript Does Not
JavaScript converts types silently. This bug is common with values that come from form fields, which are always strings. Run it to see what the compiler says:
index.ts(7,17): error TS2345: Argument of type 'string[]' is not assignable to parameter of type 'number[]'.
Type 'string' is not assignable to type 'number'.
In JavaScript this runs and prints 010205, because 0 + "10" is string concatenation. TypeScript refuses to compile until the strings are converted, for example with fromForm.map(Number).
The other big category is values that might be missing. Array.prototype.find returns undefined when nothing matches, and TypeScript makes you deal with it:
index.ts(8,13): error TS18048: 'user' is possibly 'undefined'.
The plain JavaScript version crashes at runtime with TypeError: Cannot read properties of undefined (reading 'name'). The TypeScript fix is to handle the case the compiler pointed at:
Output:
GRACE
no user with id 3
What TypeScript does not catch: logic errors (a wrong formula has the right type), and anything about data that enters the program at runtime. An API response typed as User is only as correct as the server that sent it, because the types are gone once the code runs. Check such data with runtime code.
Using JavaScript Libraries in TypeScript
Every npm package works from TypeScript, because the output is JavaScript anyway. The types for a package come from one of three places:
- The package ships its own
.d.tsfiles. Most actively maintained packages do, and you install nothing extra. - A separate
@typespackage from the community DefinitelyTyped project:npm install --save-dev @types/lodashadds types forlodash. - Nowhere. Then, with
stricton, the import itself is an error:
error TS7016: Could not find a declaration file for module 'lodash'. '/project/node_modules/lodash/lodash.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/lodash` if it exists or add a new declaration (.d.ts) file containing `declare module 'lodash';`
The fix is to install the @types package if one exists, or to describe the module yourself in a .d.ts file; the declaration files page shows how.
The Build Step
Browsers and Node.js do not type-check, so TypeScript needs a step between your source and the code that runs. There are three common setups:
tsccompiles everything. It type-checks and writes.jsfiles, usually into adistfolder. Simple, and the standard for libraries.- A bundler or dev server removes the types, and
tsc --noEmitchecks them. Vite and esbuild strip types without checking, which keeps reloads fast; the editor and a CI step run the type checker. - The runtime removes the types. Current Node.js versions, Deno and Bun run
.tsfiles directly. None of them type-checks while running, sotsc --noEmit(ordeno check) is still how you find type errors.
JavaScript needs none of this, which is its biggest practical advantage for small scripts. The cost of the TypeScript step is mostly setup, a tsconfig.json and a typescript dev dependency, and compile time; TypeScript 7's native compiler cut that time by about ten times on large projects.
Learning Curve
Everything you know about JavaScript carries over, because TypeScript's runtime is JavaScript. The new material is the type system, and it comes in layers:
- Annotations on variables, parameters and return values (
: string,: number[]). - Object types with
interfaceandtype, optional properties, unions likestring | number. - Narrowing: checking a value with
typeof,inor===so the compiler knows which case you are in. - Generics, utility types such as
Partial<T>andPick<T, K>, and advanced types for library authors.
The first two layers cover most application code. Much of the typing is inferred, so a lot of TypeScript looks like JavaScript with annotations only on function signatures.
When to Choose TypeScript or JavaScript
Is TypeScript better than JavaScript? For code that several people maintain or that lives for years, usually yes, and the industry has moved that way: by GitHub's count of monthly contributors, TypeScript overtook both JavaScript and Python in August 2025 to become the most used language on GitHub. For small scripts, plain JavaScript is often the better tool.
Choose TypeScript when:
- More than one person works on the code, or it will be maintained for months or years.
- The codebase is large enough that you cannot keep every function signature in your head.
- You refactor often: renaming a property updates every use, and the compiler lists anything left over.
- You publish a library: the
.d.tsfiles give its users autocompletion and checks. - The framework expects it. Angular apps are written in TypeScript, Next.js and Astro scaffold new projects in TypeScript by default, and Vite's React, Vue and Svelte templates each come in a TypeScript variant.
Choose JavaScript when:
- The program is a short script, a one-off experiment, or a code snippet in a browser console.
- You are learning programming for the first time and want fewer concepts at once.
- There is no build step and you do not want one. Even then,
// @ts-checkwith JSDoc gives you some checking in a plain.jsfile.
Migrating a JavaScript Project to TypeScript
A migration does not have to happen at once. The compiler accepts JavaScript next to TypeScript:
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}
With allowJs, .js files compile and can import from .ts files and the other way around. Then convert gradually:
- Rename one file from
.jsto.tsand fix the errors the compiler reports in it. - Start with the leaves (utility modules with few imports), then work inward.
- Turn on
checkJs, or add// @ts-checkat the top of individual.jsfiles, to type-check files you have not renamed yet.
In checked JavaScript files, JSDoc comments supply the types:
// @ts-check
/**
* @param {number} price
* @param {number} qty
* @returns {number}
*/
function lineTotal(price, qty) {
return price * qty;
}
lineTotal("3", 2);
// error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
Some teams stop there: JavaScript files with JSDoc types, checked by tsc, and no build step for the code itself. Others go all the way to .ts. If you turn on strict in an existing project, expect many errors at first; the strict mode page lists what each flag checks so you can enable them one at a time.
Frequently Asked Questions
What is the main difference between TypeScript and JavaScript?
TypeScript adds static types to JavaScript. You describe what each value is (name: string, items: Item[]), and the TypeScript compiler reports mistakes before the code runs. JavaScript checks nothing ahead of time: a wrong type shows up only when that line executes, often as undefined or a TypeError.
Is TypeScript better than JavaScript?
For most projects that more than one person maintains, or that live longer than a few weeks, yes: types catch whole classes of bugs, make refactoring safe and power the editor's autocompletion. For a short script, a quick prototype or a learning exercise, plain JavaScript is faster to start with and needs no build setup.
Is TypeScript faster than JavaScript?
No, and it is not slower either. TypeScript compiles to JavaScript and the types are erased, so the code that runs is the same JavaScript you would write by hand. The only extra cost is compile time during development.
Should I learn JavaScript or TypeScript first?
Learn the JavaScript basics first or together with TypeScript. All runtime behavior (variables, functions, objects, arrays, promises) is JavaScript, and TypeScript only describes it. Once you can write small JavaScript programs, adding types is a short step.
Can I use TypeScript and JavaScript in the same project?
Yes. Set "allowJs": true in tsconfig.json and the compiler accepts .js files next to .ts files. Add "checkJs": true (or a // @ts-check comment per file) to type-check the JavaScript files too, using types from JSDoc comments. That is the usual way to migrate a project file by file.