読み込み中...

【TypeScript】declare完全ガイド!10の実用コードで徹底解説

TypeScriptのdeclareを図解したイラスト付き完全ガイド TypeScript
この記事は約19分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Web開発の現代では、JavaScriptよりも型情報を持つTypeScriptが主流となりつつあります。

このTypeScriptには、JavaScriptとの差異や追加機能が多々あり、その中でもdeclareキーワードは非常に特徴的です。

この記事では、TypeScriptでのdeclareの基本的な役割や意義について詳しく解説します。

●TypeScriptのdeclareとは

TypeScriptはJavaScriptのスーパーセットであるため、JavaScriptの機能に加えて、新たな機能や概念が追加されています。

その中で、declareキーワードはTypeScriptのみに存在する特有のキーワードであり、JavaScriptには存在しません。

このキーワードの主な役割は、TypeScriptに対して特定の変数や関数、クラスなどが存在することを「宣言」することです。

このコードでは、例えば、外部ライブラリや既存のJavaScriptコードをTypeScriptで利用する際に型情報を補完するために使用します。

例えば、JavaScriptのライブラリをTypeScriptプロジェクトで利用する場合、そのライブラリが提供する関数やオブジェクトの型情報が不足していると、コンパイルエラーやランタイムエラーの原因となります。

このような場合に、declareキーワードを使用して、不足している型情報を補完することができます。

○declareの基本的な役割と意義

□型情報の補完

先述の通り、declareの最も基本的な役割は、既存のJavaScriptコードや外部ライブラリに対する型情報の補完です。

TypeScriptは型安全な言語であるため、明確な型情報が不足していると、コンパイル時にエラーが発生します。

このような状況で、declareを利用して、型情報を明示的に指定することで、安全にコードを書くことができます。

□外部ライブラリの利用

多くのJavaScriptライブラリは、元々TypeScriptのために設計されていないため、型定義ファイル(.d.ts)が別途必要になります。

これらの型定義ファイル内で、declareキーワードが頻繁に利用され、外部ライブラリのAPIや関数の型情報をTypeScriptに知らせます。

□グローバルな変数や関数の宣言

プロジェクト内でグローバルに利用される変数や関数がある場合、それらを全体で型安全に利用するために、declareキーワードを使用して、その存在と型情報を明示的に宣言します。

●declareの正しい使い方

TypeScriptを使用する開発者として、特定の変数や関数、クラスなどの型がどのように宣言されているかを正確に知ることは非常に重要です。

ここでは、TypeScriptのdeclareキーワードの正しい使い方に焦点を当て、その基本的な書き方や活用法について詳しく解説していきます。

○サンプルコード1:基本的なdeclareの書き方

TypeScriptで外部のJavaScriptライブラリやモジュールを使用する際、そのライブラリの型情報が存在しない場合、declareを使用して型情報を追加することができます。

外部のJavaScript関数externalFunctionの型宣言のサンプルコードを紹介します。

// 外部のJavaScript関数の型宣言
declare function externalFunction(param: string): number;

// 使用例
const result: number = externalFunction('test');

このコードでは、externalFunctionという関数が文字列のパラメータを受け取り、数値を返すことを宣言しています。

実際のexternalFunctionの実装はこのファイル内には存在しないため、declareを使用して型のみを宣言しています。

このコードを実行すると、文字列'test'externalFunctionに渡し、その返り値を数値としてresultに格納します。

もしexternalFunctionの実装が存在すれば、その関数が正常に動作し、期待通りの結果を返すでしょう。

○サンプルコード2:外部ライブラリの型宣言

TypeScriptは、JavaScriptのスーパーセットであり、最も大きな特徴として型システムを持っています。

しかし、JavaScriptは動的型付け言語であるため、数多くの既存のライブラリやフレームワークがTypeScriptの型システムに対応していないことがあります。

このため、TypeScriptを使って既存のライブラリやフレームワークを使用する場合、型情報を追加する必要が生じることがよくあります。

この型情報の追加を手軽に行うために、declareキーワードが用意されています。

TypeScriptの世界でJavaScriptのライブラリを利用するためには、そのライブラリの関数やメソッド、プロパティに型を与えることが求められます。

このために、多くの場合、@types/ライブラリ名という形式で提供されている型定義ファイルをインストールします。

しかし、全てのライブラリがこのような型定義ファイルを持っているわけではありません。そこで、自分で型定義を追加する場面が出てきます。

それでは、外部のJavaScriptライブラリexampleLibの関数doSomethingをTypeScriptで使用する際の型宣言の一例を紹介します。

// exampleLib.d.ts
declare module "exampleLib" {
  // このコードではdoSomething関数を使って、文字列型の引数を受け取り、戻り値として数値型を返すと定義しています。
  function doSomething(input: string): number;
}

上記のサンプルコードでは、exampleLibというモジュール全体をdeclareキーワードを用いて型宣言しています。

その中でdoSomething関数は、文字列型の引数を1つ取り、数値型を戻り値として返すという型情報を与えています。

このコードを実行すると、exampleLibdoSomething関数をTypeScriptのコード内で安全に使用することができるようになります。

例えば、次のように使用できます。

import { doSomething } from "exampleLib";

const result = doSomething("test");
console.log(result);

このとき、doSomething関数に文字列以外の型を渡そうとすると、TypeScriptのコンパイラが型エラーを検出してくれます。

○サンプルコード3:モジュール拡張の方法

TypeScriptでよく行われる操作の一つに、既存のモジュールやライブラリに型の拡張を行うことが挙げられます。

特定のライブラリが提供している型定義が、ある特定の状況下で不足していると感じた時や、独自の拡張を加えたい場面で、この手法が役立ちます。

今回は、外部ライブラリのモジュールに型を追加する方法をサンプルコードを交えて解説します。

例として、あるライブラリexampleLibが提供しているdoSomethingという関数があり、この関数に独自の型を拡張したいとします。

// 例: exampleLibが提供している関数の型
declare module 'exampleLib' {
    function doSomething(data: string): void;
}

このdoSomething関数に、新しいオプションパラメータextraOptionを追加したいと思います。

その場合、次のように型を拡張します。

// 既存のモジュールに対する型拡張
declare module 'exampleLib' {
    function doSomething(data: string, extraOption?: boolean): void;
    // extraOptionはboolean型のオプションパラメータとして追加されました。
}

このコードでは、exampleLibモジュール内のdoSomething関数に、新しいパラメータextraOptionを追加しています。

この新しいパラメータはオプションであり、boolean型を取ることができます。

このコードを実行すると、doSomething関数は2つの引数を取ることができるようになります。

第一引数はstring型のdataで、第二引数はオプションのboolean型のextraOptionです。

例えば、doSomething("test", true)のように関数を呼び出すことができるようになります。

第二引数を省略して、doSomething("test")のように呼び出すことも可能です。

●declareの応用例

TypeScriptのdeclare機能は、ある程度の基本を理解した上で、より高度な使い方を理解することで、開発効率やコードの品質を大きく向上させることができます。

特に、既存の型をカスタマイズする際に、この機能の真価が発揮されます。

ここでは、その具体的な方法とサンプルコードを通して、declareの応用例を学んでいきましょう。

○サンプルコード4:既存の型をカスタマイズ

TypeScriptの既存の型をカスタマイズする場合、型拡張を行いたいと思うことがあります。

例として、Array型に新しいメソッドを追加するシチュエーションを考えてみます。

// 既存のArray型に新しいメソッドを追加する
declare global {
  interface Array<T> {
    toJoinedString(separator: string): string;
  }
}

Array.prototype.toJoinedString = function(separator: string): string {
  return this.join(separator);
}

// 使用例
const numbers = [1, 2, 3];
console.log(numbers.toJoinedString('-')); // "1-2-3"

このコードでは、Array型に新しいメソッドtoJoinedStringを追加しています。

このメソッドは、指定したセパレータを用いて配列の要素を結合した文字列を返す機能を持っています。

サンプルコードの最後に示した使用例により、[1, 2, 3]という数字の配列を"1-2-3"という文字列に変換することができます。

ここでのポイントは、declare globalを使用してグローバルなスコープで型を拡張する方法です。

そして、この拡張した型に対応するメソッドをArray.prototypeに追加することで、すべての配列オブジェクトでこのメソッドを使用することができるようになります。

○サンプルコード5:外部ライブラリの関数拡張

TypeScriptを使用する際の大きな魅力の1つは、既存のJavaScriptライブラリをより効率的に活用する能力です。

しかし、時にはライブラリの型定義が不完全であったり、特定のプロジェクトでカスタム関数を追加したい場合があります。

そのような場面で役立つのが、外部ライブラリの関数拡張です。

ここでは、外部ライブラリの関数をTypeScriptで拡張する方法について詳しく解説します。

例として、一般的なJavaScriptのライブラリ「sampleLib」を考えます。

このライブラリにはdoSomethingという関数があり、これを拡張して新しい関数doSomethingExtraを追加したいとします。

// 外部ライブラリの型定義をインポート
import * as sampleLib from 'sampleLib';

// declareを使ってsampleLibの型を拡張
declare module 'sampleLib' {
  // 新しい関数の型定義を追加
  function doSomethingExtra(param: string): void;
}

// 実際に関数を拡張
(sampleLib as any).doSomethingExtra = function (param: string) {
  console.log('Extra: ' + param);
};

このコードでは、まずsampleLibをインポートしています。

その後、declare moduleを使用して、既存のライブラリの型を拡張しています。

ここではdoSomethingExtraという新しい関数の型定義を追加しています。

最後に、実際にdoSomethingExtra関数をsampleLibに追加しています。

この関数は、引数として文字列を受け取り、それをコンソールに表示するシンプルなものです。

このコードを実行すると、既存のsampleLibdoSomethingExtra関数が追加され、次のように使用できます。

sampleLib.doSomethingExtra('Hello TypeScript!');

上記のコードを実行すると、コンソールに「Extra: Hello TypeScript!」と表示されます。

これにより、外部ライブラリにカスタム関数を追加することが可能になります。

○サンプルコード6:グローバルな型の追加

TypeScriptを使用すると、既存のグローバルな型を拡張することも可能です。

これは特に、JavaScriptのライブラリやフレームワークを使用する際に便利です。

例として、JavaScriptのwindowオブジェクトに新しいプロパティを追加したい場合、declare globalを使用してその型定義を追加することができます。

windowオブジェクトにmyCustomPropertyという新しい文字列型のプロパティを追加するサンプルコードを紹介します。

// グローバルなWindowインターフェースを拡張
declare global {
    interface Window {
        myCustomProperty: string;
    }
}

// 新しく追加したプロパティを使用する
window.myCustomProperty = "Hello, TypeScript!";
console.log(window.myCustomProperty);

このコードでは、まずdeclare globalの中でWindowインターフェースを拡張し、myCustomPropertyという新しいプロパティを追加しています。

その後、実際にこの新しいプロパティを使用して値を代入し、コンソールに出力しています。

このコードを実行すると、Hello, TypeScript!という文字列がコンソールに表示されます。

こうした拡張は、特定のライブラリやフレームワークが提供するグローバルオブジェクトに対して、カスタムな型情報を付与する際に役立ちます。

●declareでのよくあるトラブルと解決法

TypeScriptを使用して開発を進めていると、特にdeclareを活用している場合、いくつかのトラブルに直面することが考えられます。

ここでは、declareを用いた際の典型的なトラブルとその解決策を紹介します。

○トラブル例1:型エラーの対処法

TypeScriptの魅力の一つは、静的型付けによるエラー検出能力です。

しかし、型の宣言やカスタマイズが不適切な場合、意図しない型エラーが発生することがあります。

これは特に、外部ライブラリや既存のJavaScriptプロジェクトにTypeScriptを導入した場合によく見られます。

例として、次のようなサンプルコードを考えてみましょう。

// 外部ライブラリの型定義
declare module "外部ライブラリ" {
    function sampleFunction(value: string): number;
}

import { sampleFunction } from "外部ライブラリ";

// 型エラーが発生する例
const result = sampleFunction(123); // 引数はstringである必要がある

このコードでは、sampleFunctionに数値型の引数を渡しているため、型エラーが発生します。

解決策としては、次のように正しい型の引数を渡すことで、エラーを解消できます。

const result = sampleFunction("123");

このコードを実行すると、sampleFunctionは文字列”123″を受け取り、期待する数値型の戻り値を返します。

□サンプルコード7:型エラーの解決例

では、別の典型的な型エラーとその解決策を紹介します。

下記のコードは、オブジェクトのプロパティにアクセスしようとする際の型エラーの一例です。

type User = {
    id: number;
    name?: string;
}

const user: User = {
    id: 1
};

// 型エラーが発生する例
const userName = user.name.toUpperCase();

このコードでは、nameプロパティはオプショナル(存在しない場合がある)であるため、toUpperCaseメソッドを呼び出す前にnameの存在チェックが必要です。

解決策としては、次のようにnameの存在を確認することで、安全にメソッドを呼び出せます。

const userName = user.name ? user.name.toUpperCase() : undefined;

この修正を行うと、user.nameが存在する場合は大文字に変換された文字列を、存在しない場合はundefineduserNameに代入します。

○トラブル例2:モジュールが見つからない時の対処法

TypeScriptを使用して開発を進める際、一般的には、モジュールが見つからないというエラーに直面することがあります。

これは外部ライブラリを使用している際や、自作のモジュールをimportする際に、型宣言が不足しているか、不適切な場合に発生します。

具体的には、TypeScriptはJavaScriptのスーパーセットであり、JavaScriptのライブラリやモジュールをそのままTypeScriptで使用する場合、その型情報が存在しないためエラーとなることがあります。

また、自作のモジュールで型を正しく宣言していない場合や、モジュールのパスが間違っている場合も同様のエラーが発生します。

ここでは、そのようなトラブルの解決方法を詳しく解説していきます。

□サンプルコード8:モジュールエラーの解決例

外部ライブラリsampleLibraryをimportして使用しているコード例を紹介します。

このライブラリにはTypeScriptの型宣言が存在しないため、エラーが発生します。

// main.ts
import { sampleFunction } from 'sampleLibrary';

sampleFunction();

このコードでは、sampleLibraryを使ってsampleFunctionを実行しています。

しかし、このままでは型情報が不足しているため、TypeScriptはエラーを出力します。

エラーを解決するためには、型宣言ファイル.d.tsを作成して、このライブラリの型情報を追加します。

// sampleLibrary.d.ts
declare module 'sampleLibrary' {
    export function sampleFunction(): void;
}

このコードでは、sampleLibraryモジュール内のsampleFunction関数の型を宣言しています。

この関数は何も返さない(void型)関数として宣言されています。

この型宣言ファイルをプロジェクトに追加することで、先程のエラーは解消されます。

正確には、sampleLibraryの関数が何も返さないと予測され、void型として型チェックされることとなります。

●declareのカスタマイズ方法

TypeScriptのdeclareキーワードは、型宣言に非常に便利なツールとして機能します。

しかし、その使い方を一歩進めることで、より柔軟な型カスタマイズも可能となります。

ここでは、declareを使用して、独自の型を作成する方法や、既存の型をカスタマイズする方法を詳細に解説します。

○サンプルコード9:カスタム型の作成

TypeScriptでの型作成は、型エイリアスやインターフェースを使用することで簡単に行うことができます。

しかし、既存のJavaScriptライブラリやフレームワークを利用している場合、そのライブラリが提供するオブジェクトや関数に、独自の型を追加したい場面もあるでしょう。

そのような場合にdeclareを活用することで、独自の型を追加することができます。

例として、JavaScriptのライブラリであるsampleLibがあり、そのsampleFuncという関数が存在すると仮定します。

この関数に、独自の型を追加する方法を見てみましょう。

// 既存のsampleLibの型定義
declare namespace sampleLib {
    function sampleFunc(s: string): void;
}

// 独自の型を追加
declare module sampleLib {
    interface MyCustomType {
        id: number;
        name: string;
    }

    function myFunction(param: MyCustomType): void;
}

このコードでは、sampleLibという名前空間内に、MyCustomTypeというインターフェースと、myFunctionという関数を追加しています。

こうすることで、既存のsampleLibの機能に加え、独自の型や関数を追加することができます。

このコードを実行すると、sampleLibに新しい型MyCustomTypeと新しい関数myFunctionが追加されるという結果になります。

これにより、既存のライブラリを変更することなく、独自の型や関数を安全に追加することが可能となります。

○サンプルコード10:型のマージと結合

TypeScriptの型システムの魅力の一つは、異なる型をマージや結合して新しい型を生成する柔軟性があることです。

ここでは、型のマージと結合の方法について、詳細に解説します。

TypeScriptには、&オペレータを使用して複数の型を一つに結合する方法があります。

このオペレータを使うことで、2つ以上の型を結合して新しい型を作ることができます。

これを「交差型」と呼びます。

まずは、交差型を使った基本的なサンプルコードを見てみましょう。

// 二つの異なる型を定義
type Name = {
  name: string;
};

type Age = {
  age: number;
};

// 二つの型を結合して新しい型を作成
type Person = Name & Age;

// 結合した型を使用する
const person: Person = {
  name: "田中太郎",
  age: 25
};

このコードでは、まずNameAgeという2つの型を定義しています。

その後、&オペレータを使用して、これらの型を結合して新しいPerson型を作成しています。

このようにして作成されたPerson型は、nameageの両方のプロパティを持っています。

次に、実際にPerson型を使用して、personという変数を宣言しています。

この変数には、nameageの両方のプロパティを持つオブジェクトを代入しています。

まとめ

TypeScriptはJavaScriptのスーパーセットとして、静的型付けの特徴を持ちます。

この記事を通じて、特に「declare」に関する概念や利用方法を解説しました。

今回解説したdeclareの概念やその使い方を実際の開発に活用することで、TypeScriptの持つ型の安全性をより一層高めることができるでしょう。