TypeScriptは、JavaScriptの上に静的な型システムを加えたものです。JavaScriptのプログラムはすべてTypeScriptの構文としても正しく、TypeScriptはそこに型注釈、コードの実行前にそれをチェックするコンパイラー、そして型を再び取り除くビルドの手順を加えます。実行時にはJavaScriptしかないので、両者の違いはすべて、リリース前に何がわかるかにあります。
JavaScriptで同じ関数を書くと、このコードから type Product = ...、: Product[]、: number を取り除いたものになります。型はコンパイラーとエディターに情報を加えるだけで、プログラムの動きは変えません。
TypeScriptとJavaScriptの違いを一覧で
| JavaScript | TypeScript | |
|---|---|---|
| 型システム | 動的: 型は値に属し、実行時にしかわからない | 静的: 型は宣言または推論され、コンパイル時にチェックされる |
| 型エラーが現れるとき | その行が実行されたとき(undefined、NaN、TypeError) | 入力中のエディターと、コンパイル時 |
| 動く場所 | ブラウザー、Node.js、Deno、Bunで直接 | 同じ場所で、型を取り除いたあと |
| ビルドの手順 | 不要 | tsc かバンドラー、または型を自分で取り除くランタイム |
| ファイル | .js、.mjs、.cjs | .ts、.mts、.cts、.tsx、それに型宣言の .d.ts |
| 実行速度 | 基準 | 同じ: 出力はJavaScript |
| エディターのサポート | 推論された型とライブラリの型定義による補完(不完全なこともある) | 宣言した型による補完、名前の変更、「すべての参照を検索」 |
| 学習コスト | 低い | JavaScriptに加えて型システム |
| 標準 | TC39によるECMAScript | ECMAScriptに追随するMicrosoftのオープンソースプロジェクト |
同じコードを両方の言語で
JavaScriptの関数です。user がどんな形でなければならないかは、どこにも書かれていません。
function greeting(user) {
return `Hello, ${user.firstName} ${user.lastName}`;
}
greeting({ firstname: "Ada", lastName: "Lovelace" });
// "Hello, undefined Lovelace", no error anywhere
TypeScript版では形を一度書くだけで、タイプミスはコードの実行前に報告されます。
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'?
構文の違いは型注釈だけです。TypeScriptは独自の宣言もいくつか加えます(interface、type、enum、Array<string> のようなジェネリクス、private のようなアクセス修飾子)が、文、演算子、組み込みオブジェクトはJavaScriptのものです。
JavaScriptでは見つからず、TypeScriptが見つけるもの
JavaScriptは黙って型を変換します。このバグは、常に文字列であるフォームの入力値でよく起こります。実行してコンパイラーの出力を見てください。
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'.
JavaScriptならこれは実行され、010205 と表示されます。0 + "10" が文字列の連結になるからです。TypeScriptは、たとえば fromForm.map(Number) で文字列を変換するまでコンパイルを拒否します。エラーの Type 'string' is not assignable to type 'number'. は「string型をnumber型に代入できない」という意味です。
もう1つの大きな分類は、存在しないかもしれない値です。Array.prototype.find は一致するものがないと undefined を返し、TypeScriptはその場合の処理を求めます。
index.ts(8,13): error TS18048: 'user' is possibly 'undefined'.
普通のJavaScript版は、実行時に TypeError: Cannot read properties of undefined (reading 'name') でクラッシュします。TypeScriptでの直し方は、コンパイラーが指摘したケースを処理することです。
出力:
GRACE
no user with id 3
TypeScriptが見つけ ない もの: ロジックの誤り(間違った計算式でも型は正しい)と、実行時にプログラムに入ってくるデータに関すること全般です。User と型を付けたAPIのレスポンスが正しいかどうかは、それを送ったサーバー次第です。コードが実行されるときには型はもう消えているからです。そうしたデータは実行時のコードでチェックしてください。
TypeScriptでJavaScriptのライブラリを使う
出力はどのみちJavaScriptなので、npmのパッケージはすべてTypeScriptから使えます。パッケージの型は次の3つのどこかから来ます。
- パッケージ自身が
.d.tsファイルを含んでいる。 活発にメンテナンスされているパッケージの多くはそうで、何も追加でインストールする必要はありません。 - コミュニティのDefinitelyTypedプロジェクトの、別の
@typesパッケージ。npm install --save-dev @types/lodashでlodashの型が加わります。 - どこにもない。 その場合、
strictがオンならインポート自体がエラーになります。
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';`
直し方は、@types パッケージがあればそれをインストールするか、.d.ts ファイルでモジュールを自分で記述することです。やり方は型定義ファイルのページで解説しています。
ビルドの手順
ブラウザーやNode.jsは型チェックをしないので、TypeScriptにはソースコードと実行されるコードの間にひと手間が必要です。よく使われる構成は3つあります。
tscがすべてをコンパイルする。 型チェックをして、ふつうはdistフォルダーに.jsファイルを書き出します。シンプルで、ライブラリの標準的なやり方です。- バンドラーや開発サーバーが型を取り除き、
tsc --noEmitがチェックする。 Viteやesbuildはチェックせずに型を取り除くので、再読み込みが速く保たれます。型チェッカーはエディターとCIのステップで実行します。 - ランタイムが型を取り除く。 現在のNode.js、Deno、Bunは
.tsファイルを直接実行します。どれも実行中に型チェックはしないので、型エラーを見つけるにはやはりtsc --noEmit(またはdeno check)を使います。
JavaScriptにはこのどれも必要なく、それが小さなスクリプトでの一番の実用的な利点です。TypeScriptの手順のコストは、主に設定(tsconfig.json と開発用依存の typescript)とコンパイル時間です。TypeScript 7のネイティブコンパイラーは、大規模なプロジェクトでその時間をおよそ10分の1にしました。
学習コスト
TypeScriptの実行時の動きはJavaScriptなので、JavaScriptについて知っていることはすべてそのまま使えます。新しく学ぶのは型システムで、段階的に進められます。
- 変数、引数、戻り値への型注釈(
: string、: number[])。 interfaceとtypeによるオブジェクトの型、省略可能なプロパティ、string | numberのようなユニオン型。- 型の絞り込み:
typeof、in、===で値をチェックし、どのケースにいるかをコンパイラーに知らせる。 - ジェネリクス、
Partial<T>やPick<T, K>のようなユーティリティ型、ライブラリ作者向けの高度な型。
アプリケーションのコードの大部分は、最初の2段階でカバーできます。型の多くは推論されるので、TypeScriptのコードの多くは、関数のシグネチャにだけ注釈を付けたJavaScriptのように見えます。
TypeScriptとJavaScriptの使い分け
TypeScriptはJavaScriptより優れているのでしょうか。複数人で保守するコードや何年も使われるコードなら、たいていはそうで、業界もその方向に進んでいます。GitHubの月間コントリビューター数の集計では、TypeScriptは2025年8月にJavaScriptとPythonの両方を抜いて、GitHubで最も使われている言語になりました。小さなスクリプトなら、普通のJavaScriptのほうが適していることがよくあります。
TypeScript を選ぶのはこんなとき:
- 複数人でコードを扱う、または数か月から数年保守する。
- すべての関数のシグネチャを覚えていられないほどコードベースが大きい。
- リファクタリングが多い。プロパティ名を変えるとすべての使用箇所が更新され、残りはコンパイラーが一覧にします。
- ライブラリを公開する。
.d.tsファイルが利用者に補完とチェックを提供します。 - フレームワークが前提にしている。AngularのアプリはTypeScriptで書かれ、Next.jsとAstroは新しいプロジェクトを標準でTypeScriptで作成し、ViteのReact・Vue・SvelteのテンプレートにはそれぞれTypeScript版があります。
JavaScript を選ぶのはこんなとき:
- 短いスクリプト、使い捨ての実験、ブラウザーのコンソールで試すコード。
- 初めてプログラミングを学んでいて、一度に覚える概念を減らしたい。
- ビルドの手順がなく、加えたくもない。その場合でも、JSDocと
// @ts-checkを使えば普通の.jsファイルである程度のチェックができます。
JavaScriptのプロジェクトをTypeScriptに移行する
移行は一度に行う必要はありません。コンパイラーはTypeScriptと並んでJavaScriptも受け付けます。
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}
allowJs を付けると .js ファイルもコンパイルされ、.ts ファイルからインポートすることも、その逆もできます。あとは少しずつ変換します。
- ファイルを1つ
.jsから.tsに名前を変え、そこでコンパイラーが報告するエラーを直す。 - 末端(インポートの少ないユーティリティのモジュール)から始めて、内側へ進む。
checkJsをオンにするか、個々の.jsファイルの先頭に// @ts-checkを加えて、まだ名前を変えていないファイルも型チェックする。
チェック対象のJavaScriptファイルでは、JSDocコメントが型を与えます。
// @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'.
そこで止めるチームもあります。JSDocの型を持つJavaScriptファイルを tsc でチェックし、コード自体のビルドの手順は持たないやり方です。すべてを .ts にするチームもあります。既存のプロジェクトで strict をオンにすると、最初は大量のエラーが出ると考えてください。strictモードのページに各フラグが何をチェックするかがまとめてあるので、1つずつ有効にしていけます。
よくある質問
TypeScriptとJavaScriptの主な違いは何ですか?
TypeScriptはJavaScriptに静的な型を加えます。それぞれの値が何かを書き(name: string、items: Item[])、TypeScriptのコンパイラーがコードの実行前にミスを報告します。JavaScriptは事前に何もチェックしません。型の間違いはその行が実行されたときにだけ現れ、たいていは undefined や TypeError になります。
TypeScriptはJavaScriptより優れていますか?
複数人で保守するプロジェクトや、数週間以上使われるプロジェクトの多くでは、そうです。型はさまざまな種類のバグを見つけ、リファクタリングを安全にし、エディターの補完を支えます。短いスクリプト、手早いプロトタイプ、学習用の練習なら、普通のJavaScriptのほうがすぐに始められ、ビルドの設定もいりません。
TypeScriptはJavaScriptより速いですか?
いいえ。遅くもありません。TypeScriptはJavaScriptにコンパイルされ、型は消去されるので、実行されるのは手で書くのと同じJavaScriptです。追加のコストは開発中のコンパイル時間だけです。
JavaScriptとTypeScriptのどちらを先に学ぶべきですか?
JavaScriptの基本を先に、またはTypeScriptと一緒に学びましょう。実行時の動き(変数、関数、オブジェクト、配列、Promise)はすべてJavaScriptで、TypeScriptはそれを記述するだけです。小さなJavaScriptのプログラムを書けるようになれば、型を加えるのはすぐです。
同じプロジェクトでTypeScriptとJavaScriptを一緒に使えますか?
使えます。tsconfig.json で "allowJs": true を設定すると、コンパイラーは .ts ファイルと並んで .js ファイルも受け付けます。"checkJs": true(またはファイルごとの // @ts-check コメント)を加えると、JSDocコメントの型を使ってJavaScriptのファイルも型チェックされます。これがプロジェクトをファイルごとに移行するふつうの方法です。