Updated on October 21, 2024
·Originally published on October 24, 2023
TypeScript provides developers many advantages over JavaScript: you can now identify and enforce data types, better communicate the purpose of code, and streamline your development process with enhanced developer tools that leverage TypeScript’s enhancements over JavaScript.
This article explains the difference between TypeScript and JavaScript, how to take advantage of the new features, and how to migrate your projects.
JavaScript is a high-level interpreted programming language that’s been around since 1995 and is widely used in web development.
It was originally developed for adding interactivity and functionality to HTML web pages. It can manipulate the Document Object Model (DOM), making it possible to create dynamic and responsive user interfaces that run in the web browser.
Although JavaScript is best known for being a client-side language, developers’ widespread familiarity with it led to it being used server-side using Node.js, and to develop mobile and desktop apps using frameworks like Ionic and Electron.
TypeScript is a programming language that's a superset of JavaScript — building on top of it to add static typing so you can catch errors during development instead of at runtime.
It was developed by Microsoft to address the difficulties of maintaining large applications written in JavaScript, and has been expanded to add a number of modern developer-friendly features.
In addition to static typing, TypeScript also adds features like interfaces, generics, type inference, access modifiers, and other constructs not present in standard JavaScript.
TypeScript compiles to plain JavaScript, so anything that works in JavaScript works in TypeScript, including third-party JavaScript libraries and any code you've already written in JavaScript.
Both TypeScript and JavaScript are widely used for web development. As they are so closely related, they're often confused, but the key differences between TypeScript and JavaScript are as follows:
For those of us who are coming from JavaScript, we may not have seen types before (JavaScript, famously, does not enforce variable types). However, static typing is a feature that can be found in other languages and is considered a vital feature by many developers, helping them write better code and build more reliable apps.
Static typing is a programming language feature that lets you explicitly specify the data types of variables, function parameters, and return values in advance, so they are known at compile time. For TypeScript, this means the code is checked for type-related errors when it's compiled to JavaScript, so type-related errors are caught before they reach users.
By contrast, JavaScript has dynamic typing. This is where data types are determined and checked at runtime, meaning that if there is a type-related error (like trying to multiply a string by an integer), it won't be encountered until someone finds the bug by running the code — which could be one of your users in production.
Static typing in TypeScript ensures that variables are used in ways consistent with their defined types, preventing certain types of errors that can occur due to incorrect type usage. This solves a big problem in JavaScript development: bugs or unexpected input can cause a variable to be mistyped, resulting in improper handling by your application.
An example of a type error is shown in the below JavaScript snippet. This code assigns a string to the age variable, even though it was originally supposed to be a number. This then leads to a runtime error when the code later tries to use age in a numeric context.
let age = 25;
age = "twenty six";
console.log(age * 2); // runtime error: "NaN"
The above JavaScript causes a NaN error in the browser.
Using a string in a numerical context results in NaN (Not a Number), a special variable that indicates an operation has failed due to a number conversion.
With TypeScript, adding a static number type to the age variable causes the compiler to immediately raise the issue that “Type ‘string
’ is not assignable to type ‘number
’” — allowing you to fix the issue before a user encounters it at runtime.
let age: number = 25;
age = "twenty six"; // compiler error: "Type 'string' is not assignable to type 'number'."
The above TypeScript causes an error earlier — before runtime.
Static typing offers several key benefits:
Early error detection: Type-related errors are caught at compile time, helping developers identify and fix issues before the code is executed. Many modern code editors (e.g., Visual Studio Code) support the TypeScript compiler running automatically in the background, meaning errors are caught as you write code.
Improved code quality: Type annotations make your code self-documenting and easier to understand.
Enhanced tooling: IDEs and code editors can provide better auto-completion, code navigation, and refactoring suggestions based on type information.
Readability and maintainability: Explicit types make code more readable and understandable, especially in larger projects.
It's clear that TypeScript offers many advantages over JavaScript, having been designed to solve specific problems that JavaScript developers commonly face. However, there are still times when using plain old JavaScript makes more sense.
When learning to code for the first time: Beginner developers may prefer to experiment with JavaScript's flexible code without being overwhelmed with extra information.
For ad-hoc scripts or automation tasks: When you're automating tasks or writing quick, throwaway scripts, JavaScript is quicker to set up and get started with.
When working on legacy codebases: If you're working on a very old codebase that's already written in JavaScript, it may not be worth it to try to convert it all to TypeScript, which could require significant rewrites or refactoring, or even break existing functionality.
You should use TypeScript whenever you can — the advantages it provides are numerous, and in any application of any scale, it provides you with a better developer experience that results in more reliable code.
This is particularly important for complex projects that involve collaboration between teams.
TypeScript vs. JavaScript has a clear winner, and it's TypeScript, particularly if you're working on large collaborative projects. Here's why TypeScript can be a game-changer for many developers:
Modern code editors like Visual Studio Code can provide intelligent auto-completion options as you type.
Reduced errors: Static typing helps catch type-related errors at compile time, preventing many common mistakes from reaching runtime. This reduces the chances of introducing bugs that don't cause runtime errors, but that cause unexpected behavior.
Improved tooling and IDE support: TypeScript provides developers with robust tooling, including code completion, navigation, and refactoring suggestions. These features are invaluable when dealing with large and complex codebases, as they help developers understand the code, debug easily, and make changes more efficiently.
Enhanced collaboration: Type annotations provide clear interfaces for functions and modules, making it easier for development team members to understand how different parts of the codebase interact. This leads to smoother collaboration, as different developers can work on different parts of the project without stepping on each other’s toes.
Refactoring: Large projects often require refactoring to improve code quality and maintainability. TypeScript’s type system makes refactoring safer by alerting developers to potential issues when they change code. This minimizes the risk of breaking existing functionality.
Code quality and consistency: TypeScript encourages a higher level of code quality and consistency across the project by enforcing type standards and providing clearer guidelines for code structure.
Code maintainability: With TypeScript's static typing, it’s easier to understand the data types of variables, function parameters, and return values. This leads to more self-documenting code that is easier to read, which is crucial for maintaining and extending projects over time.
An example of self-documenting code.
In the above image, TypeScript is providing type information to show that both length and width must be numbers, which causes an error later if one is later entered as a string. In JavaScript, the final line of code would run with no errors (as the string “5
” would be silently converted to the number 5), creating a source of possible bugs later on during runtime.
You don’t need to take an all-or-nothing approach between TypeScript and JavaScript, as you can adopt TypeScript incrementally. You might decide to just start adding types to any new code that you add, or the most error-prone parts of your code to begin with, meaning the learning curve can be as gentle as you like.
To migrate an existing JavaScript project to TypeScript, ensure you have the latest versions of Node.js and npm installed on your machine. Once you've confirmed that, follow these steps:
First, install TypeScript in your project by running the following command:
npm install typescript --save-dev
Next, create a tsconfig.json
file, which tells the TypeScript compiler how to compile your code. Do this by running:
npx tsc --init
This creates a basic TypeScript configuration. As you're migrating from JavaScript, next you'll need to add support for your existing JavaScript files.
To allow TypeScript to compile .js
files (which you'll need to do until your project is fully converted to TypeScript), add "allowJs": true to the compilerOptions
object in tsconfig.json
:
You can do this by changing the file extension. Rename *.js
files to *.ts
or *.jsx
files to *.tsx
.
Now run the compiler against your new file with the npx tsc path/to/your/file.ts
command. TypeScript will now type check the files. Even without having added any type annotations at all, it can still sometimes alert you to unnoticed JavaScript bugs or basic type incompatibilities.
You can even convert all your files at once (or do them in batches) and then run npx tsc
to compile them all at once. How many files to group and convert at once is a personal choice, balancing efficiency with the ability to notice any issues raised by the compiler.
Avoid renaming files that have been auto-generated (such as those in .next/
) or files from external libraries or installed dependencies (like those in node_modules/).
You don’t have to add type annotations all at once. Start small by adding types for function parameters and return values. For instance:
Then, you can start adding interfaces and type aliases, and start leveraging more of TypeScript's features to catch more potential errors early.
Note that if you’re using any third-party libraries, you'll need type definitions for them. You can find definitions for the most popular (and in some cases, unpopular) JavaScript libraries in the DefinitelyTyped repository.
After migrating your files, run your test suites to check that everything still works. This will help you catch any breaking changes introduced during the migration.
At Contentful, we use TypeScript for our frontend software development to take advantage of all the safety and utility features described above when working on React, Angular, or Next.js projects.
You can integrate Contentful with your TypeScript project via our REST or GraphQL APIs to provide an instant, fully configurable content back end for managing and delivering all of your text, images, videos, and other content from our high-speed CDN. Using TypeScript in conjunction with GraphQL will keep your frontend code aligned with your content model, ensuring reliable applications that provide engaging user experiences (while keeping you sane during the development process with improved typing and debugging).
For more information, check out the Contentful Developer Documentation or dive into our sections onTypeScript and JavaScript.
Subscribe for updates
Build better digital experiences with Contentful updates direct to your inbox.