TypeScript vs JavaScript: Key Differences with Examples

TypeScript checks types at compile time and outputs plain JavaScript; JavaScript checks them only at runtime. A side-by-side comparison with real compiler errors.

The main difference between TypeScript and JavaScript is when type mistakes are found. JavaScript checks types only while the program runs, so passing a string where a number belongs shows up as a wrong result or a crash in production. TypeScript adds type annotations to JavaScript and checks them at compile time, before the code runs. After the check, the TypeScript compiler removes the types and outputs plain JavaScript, so both languages run on the same engines at the same speed.

We use TypeScript for apps that a team maintains for years, such as the front end of a web shop, and plain JavaScript for small scripts and quick prototypes.

The following example shows most of the differences in a few lines of code, and the comments say what each language does with each line. It uses TypeScript 7.0.2 and Node.js 22.

// 1. Type annotation (TypeScript only; removed from the JavaScript output)
let age: number = 37;

// 2. Assigning another type
// age = "thirty";                            // TypeScript: error TS2322, JavaScript: allowed

// 3. Type inference
let city = "Delhi";                           // type string, no annotation needed

// 4. Interface (TypeScript only; emits no JavaScript)
interface Person { name: string; age: number }
const raj: Person = { name: "Raj", age: 35 };

// 5. Misspelled property
// const typo = raj.nmae;                     // TypeScript: error TS2339, JavaScript: undefined

// 6. Same runtime: types do not change values
const joined = 5 + "10";                      // joined = "510" in both languages

// 7. No runtime type checks
const data: Person = JSON.parse('{"name":"John","age":"40"}');
const next = data.age + 1;                    // next = "401", not 41

Notice that steps 6 and 7 behave the same in both languages, because TypeScript types do not exist at runtime.

Next, we compare both languages in one table and go through the differences in typing, error detection, the build step and runtime behavior. After that, we cover the features only TypeScript has and how to mix both languages in one project.

1. TypeScript and JavaScript Compared in One Table

TypeScript is a superset of JavaScript, which means every JavaScript feature works in TypeScript, and TypeScript adds a type system and a few extra constructs on top. Because the output is JavaScript, the runtime rows of the table are the same for both languages; the differences are all in the development and build rows.

AspectJavaScriptTypeScript
TypingDynamic: a variable can hold any value, checked at runtimeStatic: types are checked at compile time, then removed
When type errors appearAt runtime, often as a wrong valueIn the editor and during the build
File extensions.js, .mjs, .cjs, .jsx.ts, .mts, .cts, .tsx
Build stepNone neededtsc or another tool converts it to JavaScript
Runs directly in browsersYesNo, browsers run the compiled JavaScript
Runs directly in Node.jsYesYes since Node.js 22.18 for erasable syntax, without type checking
Runtime speedSame engineSame engine; the output is ordinary JavaScript
Type information at runtimetypeof and instanceof onlySame; TypeScript types do not exist at runtime
Editor supportAutocompletion from inferenceAutocompletion, safe renaming and error checking from declared types
Learning costLowerType syntax and compiler settings to learn

2. Dynamic Typing vs Static Typing

In JavaScript, a type belongs to a value, not to a variable. A variable that holds a number can hold a string on the next line, and typeof reports whatever the variable holds at that moment.

let temp = 42;
const type1 = typeof temp;                    // type1 = "number"

temp = "Hello";                               // allowed
const type2 = typeof temp;                    // type2 = "string"

In TypeScript, a variable has a fixed type, either written as an annotation or inferred from its first value, so assigning a value of another type is a compile error.

let temp: number = 42;
temp = "Hello";
dyn.ts(2,1): error TS2322: Type 'string' is not assignable to type 'number'.

When a variable must accept both kinds of values, we say so with a union type. The difference from JavaScript is that the choice is written down and checked.

let temp: number | string = 42;
temp = "Hello";                               // allowed

3. Errors Found Before the Code Runs

Static typing catches mistakes that JavaScript runs without complaint. Each line of the next snippet is valid JavaScript, and JavaScript would run it with one of these results.

  • The expression x + y produces the string “510”.
  • Printing p.nmae shows undefined.
  • Calling raj.toFixed() crashes with a TypeError when the key is missing.
  • Calling area(5) multiplies by undefined and gets NaN.

For example, a checkout page that adds a shipping fee from a form field to a cart total of 50 shows “5010” instead of 60. TypeScript reports all of these mistakes during the build.

const x: number = 5;
const y: string = "10";
const sum: number = x + y;

interface Person { name: string; age: number }
const p: Person = { name: "Raj", age: 35 };
console.log(p.nmae);

const ages = new Map<string, number>([["Raj", 35]]);
const raj = ages.get("Raj");
console.log(raj.toFixed());

function area(width: number, height: number): number {
  return width * height;
}
area(5);
errors.ts(3,7): error TS2322: Type 'string' is not assignable to type 'number'.
errors.ts(7,15): error TS2339: Property 'nmae' does not exist on type 'Person'.
errors.ts(11,13): error TS18048: 'raj' is possibly 'undefined'.
errors.ts(16,1): error TS2554: Expected 2 arguments, but got 1.

The first error is often misread. TypeScript allows x + y with a number and a string, because JavaScript defines that operation as string concatenation, so the error comes from assigning the resulting string to a variable declared as number. The third error comes from strict mode, because Map.get() returns number | undefined, and TypeScript makes us handle the missing case. In JavaScript, the same code throws “Cannot read properties of undefined” when the key is missing.

4. The Compile Step and How Each Language Runs

A browser or Node.js executes JavaScript source as it is, whereas TypeScript source first goes through a compiler, which checks the types and writes JavaScript. For most TypeScript code, compiling only deletes the type syntax, but a few TypeScript features generate code, and enum is the common one.

interface Order { id: number; total: number }

enum Status { New, Shipped }

const order: Order = { id: 1, total: 250 };
const current: Status = Status.Shipped;       // current = 1

Compiling the file with tsc –target es2024 order.ts produces order.js.

"use strict";
var Status;
(function (Status) {
    Status[Status["New"] = 0] = "New";
    Status[Status["Shipped"] = 1] = "Shipped";
})(Status || (Status = {}));
const order = { id: 1, total: 250 };
const current = Status.Shipped;

The interface and both type annotations are gone, and the enum became an object built by a function. The generated enum code matters for the newer way to run TypeScript.

Since Node.js 22.18, node order.ts runs a TypeScript file without a build step by replacing type annotations with spaces, a feature called type stripping. Type stripping works for code like the Order interface, but it rejects the enum with ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX, and it never checks types. The compiler option erasableSyntaxOnly reports such constructs during the build. Type stripping and a tsc build are the two ways of running a TypeScript file, and tsc itself is a source-to-source compiler, also called a transpiler.

5. Runtime Behavior and Performance

TypeScript code runs as fast as the JavaScript it compiles to, because that JavaScript is what the engine executes, and TypeScript adds no runtime library and no runtime checks. The real costs are elsewhere.

  • A build step
  • Compile time in large projects
  • Time to write types

TypeScript 7 reduced the compile-time cost a lot, since its native compiler is several times faster than TypeScript 6.

The other side of type erasure is that TypeScript cannot check data that arrives while the program runs. JSON.parse() returns the type any, which turns off checking, so the compiler accepts any declared type for the result.

const data: Person = JSON.parse('{"name":"John","age":"40"}');
const next = data.age + 1;                    // next = "401", not 41

The JSON contains the age as a string, so next is the string “401”. For data from an API or a form, we declare it as unknown and check its shape before using it. A function that returns value is Person is a type guard, so when it returns true, the compiler treats the value as a Person.

const data: unknown = JSON.parse('{"name":"John","age":"40"}');

function isPerson(value: unknown): value is Person {
  return typeof value === "object" && value !== null
    && typeof (value as Person).name === "string"
    && typeof (value as Person).age === "number";
}

const valid = isPerson(data);                 // valid = false, age is a string

Larger projects often use a validation library such as Zod or Valibot for this, which builds the check and the type from one schema.

6. Features That Exist Only in TypeScript

Several features that TypeScript introduced early are now standard JavaScript, such as classes, modules, optional chaining (?.), nullish coalescing (??) and private #fields. What remains TypeScript-only is the type system and a few constructs around it.

// 1. Type alias with a union of literal types
type Size = "small" | "medium" | "large";
const size: Size = "medium";

// 2. Generic function
function firstItem<T>(items: T[]): T | undefined {
  return items[0];
}
const first = firstItem(["apple", "banana"]); // first = "apple", type string | undefined

// 3. Class with access modifiers
class Counter {
  private count = 0;
  readonly name: string;
  constructor(name: string) { this.name = name; }
  increment(): number { return ++this.count; }
}
const visits = new Counter("visits");
const count = visits.increment();             // count = 1

// 4. satisfies: check the shape, keep the literal types
const prices = { apple: 5, banana: 3 } satisfies Record<string, number>;
const applePrice = prices.apple;              // applePrice = 5

Generics let one function work with many types while keeping the type of the result, so firstItem() returns string | undefined for a string array and number | undefined for a number array. The private and readonly modifiers are checked only at compile time; at runtime the field is an ordinary property. The satisfies operator checks that a value matches a type without changing the value’s inferred type, so prices.apple stays a known property.

7. Using JavaScript and TypeScript in One Project

TypeScript does not require a rewrite, because with the allowJs option, .js and .ts files live in the same project and import each other, so a team can convert one file at a time. For JavaScript libraries, type definitions come from .d.ts files, which many npm packages include, and the DefinitelyTyped project publishes the rest as @types/… packages, such as @types/node.

We can even get type checking without TypeScript syntax, because the TypeScript compiler reads types from JSDoc comments in .js files, and a // @ts-check comment at the top turns on checking for that file in editors such as VS Code (or checkJs in tsconfig.json for the whole project).

// @ts-check

/**
 * @param {number} price
 * @param {number} quantity
 * @returns {number}
 */
export function total(price, quantity) {
  return price * quantity;
}

const amount = total(5, 3);                   // amount = 15

When we change the last line to total(5, “3”), the compiler reports the same kind of error as in a .ts file.

price.js(12,25): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

TypeScript 7 reworked its JSDoc support to match how it checks .ts files, so some older Closure-style JSDoc patterns are no longer recognized. There is also a TC39 proposal to add type annotations to JavaScript itself, which engines would ignore like comments. The type annotations proposal has been at Stage 1 since 2022, so it is not part of JavaScript today.

8. Choosing Between TypeScript and JavaScript

For most new projects that more than one person maintains, TypeScript is the better default. Errors move from production to the editor, and refactoring is safer because the compiler finds every place a change breaks. The types also document what each function expects. Angular uses TypeScript by default, and the starter templates of React (with Vite), Vue and Next.js offer it. The cost is a build step and the time to learn the type system.

Plain JavaScript still fits in a few places.

  • A small script or a quick prototype that one person writes and soon throws away gains little from types.
  • A project without a build step can use JavaScript with JSDoc types and // @ts-check, and still get most of the checking.
  • Code that must run unchanged in an environment without TypeScript tooling stays in JavaScript.

For Node.js scripts, the line between the two has become thinner. Node.js 22.18+ runs erasable TypeScript without a build step, so a script can use type annotations and be type-checked separately with tsc –noEmit.

9. Example Project With TypeScript and JavaScript Files

Both languages are side by side in the TypeScript vs JavaScript examples. The TypeScript snippets are in the src folder, and the JavaScript examples (dynamic-typing.js and price.js) are in the js folder. The project is pinned to TypeScript 7.0.2; any Node.js 22 release runs it.

npm install
npm start          # compile the TypeScript files and run them
npm run js         # run the JavaScript files with Node.js

The npm start output for the quick-reference block shows the runtime values from the comments, including the string results “510” and “401”.

=== Quick reference ===
age = 37 | city = Delhi string | raj = Raj
joined = "510"
next = "401"

10. Conclusion

TypeScript and JavaScript run the same code at the same speed; the difference is that TypeScript checks types before the program runs and JavaScript does not. The type check catches mistakes such as wrong arguments or a missing undefined check during the build, at the price of a compile step and some type syntax. TypeScript types disappear at runtime, so data from outside the program still needs runtime validation in both languages. For team projects, TypeScript is the usual choice today; for small scripts, JavaScript with JSDoc types or Node.js type stripping covers most needs.

11. References

The TypeScript handbook explains the type system, and the Node.js documentation describes running .ts files without a build step.

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.