12
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

un-T factory! XAAdvent Calendar 2024

Day 9

TypeScriptの型定義ファイル(d.ts)を読み書き

Last updated at Posted at 2024-12-09

はじめに

この記事はこのような方を対象にしてます。

  • TSは普段使っているけどd.tsファイルの読み方がいまいちわからない。
  • d.tsファイルはなんとなく読み書きできるけど記述するときにつまづく時がある。

d.tsファイル、意外とややこしい

「意外とややこしい」というのは個人の所感にはなりますが、そう思っている人もきっといるはず。。。

d.tsファイルは端的にいうと、明示的に型付されていないJSのモジュールに対して、型をつけてTSでハッピーになるためのファイルです。

実際にd.tsファイルに触れる機会といえば、TSのプロジェクトでIDEやエディタから定義ジャンプして型を確認したり、ライブラリ作者やコミッターであれば、d.tsファイルを生成したり、記述したりすることがあるでしょう。

しかし、実際にd.tsファイルを記述する機会は人によっては少なくい場合も多く、自分自身d.ts特有のキーワードや記述に困惑することもよくあったので、備忘録も兼ねてこの記事にまとめてみた。

d.tsファイルを読み書きする前に

まず、d.tsファイルを読み書きするのに、前提として重要なポイントがあります。

ファイルがモジュールなのかスクリプトなのか

この前提があることで、d.tsのルールがより理解できるでしょう。

モジュールとは

意味合いとしてはJSのモジュールシステムとほぼ同じです。TypeScriptのコンパイラはexportorimportキーワードを見つけると、そのファイルはモジュールファイルであると認識します。モジュールのコードはどこからでもアクセスできるものではなく、明示的にexportし、使用するファイルからimportしてやる必要があります。

スクリプトとは

例えば、jQueryを使用したことがる人はこのような記述をしたことがあるのではないでしょうか。

index.html
<script src="/path/to/jquery.min.js"></script>
<script src="/path/to/main.js"></script>
main.js
$(function () {
	const $element = $('.js-target-element');
	// do something...
});

このコードスニペットでは、$のjQueryオブジェクトがimportされることなく、参照されているが、これはつまり、jquery.min.jsを実行したことで、windowオブジェクトからjQueryオブジェクトが生えた状態と言えるわけです。ちなみに、TSの世界ではUMDモジュールという名前がよく出てくるが、windowオブジェクトにモジュールを生やすのと、exportsオブジェクトにモジュールを代入するCommonJSを1ファイルで兼用することができる仕組みのことです。UMDモジュールではCommonJS環境であればexportsオブジェクトに、ブラウザ環境ではwindowオブジェクトにモジュールを生やします。

このモジュールスクリプトという2つの世界観に分けてd.tsについて書いていく。

モジュールのd.ts

基本形

前述した通り、モジュールにはexportimportキーワードが必要す。例えば、以下のようなJSモジュールを想定する。

mod.js
export function sum(num1, num2) {
	return num1 + num2	
}

export function sayHello(name) {
	console.log(`Hello, ${name}!`)
}

d.tsは以下

mod.d.ts
export function sum(num1: number, num2: number): number;

export function sayHello(name: string): void;

ここまでは想像に難くないはず。

declare global

ライブラリの型定義でよく見かけるdeclare globalから始まる記述について。

その名の通り、グローバルに型定義をするが、モジュール内でのみ使用可能。スクリプトd.tsではdeclare globalを記述できません。例えば、下記のコードを考えてみましょう。

mod.js
window.hoge = hoge;

export function hoge() {
	//
}
main.ts
import './mod';

window.hoge()

例に挙げたmod.jsの型定義ファイルmod.d.tsを記述するとしたら下記。

mod.d.ts
declare global {
	interface Window {
		hoge: typeof hoge
	}
}

export function hoge(): void

export as namespace

export as namespace。こちらもグローバルスコープからアクセスできるものを定義するキーワードだが、モジュールd.tsでのみ使用可能。これによってグローバルスコープから定義にアクセスできる。どれにアクセスできるかは、参照しているファイルがモジュールかスクリプトかによって異なってくる。

型定義

mod.d.ts
export function hoge(args: Hoge.Args): void

interface Args {
	foo: string;
	bar: boolean;
}

export as namespace Hoge

モジュールからは型定義にのみアクセスできる。

main.ts
import { hoge } from './mod';

const hogeArgs: Hoge.Args = {
	foo: '...',
	bar: false,
};

hoge(hogeArgs);

スクリプトからは型定義以外にもアクセスできる。

script.ts
const hogeArgs: Hoge.Args = {
	foo: '...',
	bar: false,
};

Hoge.hoge(hogeArgs);

スクリプトのd.ts

スクリプトのd.tsの記述は比較的シンプルです。exportimportを含まないファイルのこと。

サードパーティースクリプトやアナリティクスのコードに対して定義することが多い。TypeScriptコンパイラにグローバルからアクセスできるものとして宣言する。

script.d.ts
const dataLayer: unkwon[];

function doSomething(): void;

interface Buzz {
	a: string;
	b: string;
}
main.ts
function handleClick() {
	dataLayer.push({ event: '...' });
	doSomething();
}

const buzz: Buzz = { a: '...', b: '...' };

尚、グローバルスコープのinterfaceを拡張する場合、モジュールと違ってdeclare globalの記述は不要。

script.d.ts
interface Window {
	hoge(): void
}
main.ts
window.hoge();

既存のモジュールの型を拡張

既存のモジュールのinterfaceに新しいプロパティを追加したいとなったとする。(Module Augmentationという)例えば元のモジュールの型定義がこちらだった場合、

original.d.ts
export interface Hoge {
	a: string;
	b: string;
}

export interface Foo {
	a: number;
	b: number;
}

これはモジュールのimport先のファイルで意図通りにモジュールの型定義を上書きできない。

extend.d.ts
declare module 'original' {
	interface Hoge {
		c: string
	}
}

この例ではスクリプトと認識されるため、Module Augmentationではなく、Ambient Moduleになる。

extend.d.ts
declare module 'original' {
	interface Hoge {
		c: string
	}
}

export {} // 追加

exportキーワードを追加して、TypeScriptコンパイラにモジュールと認識させることが必要。

最後に

別のプロジェクトではエラーにならなかったのに、今のプロジェクトで同じ型定義をしようとしたらなぜかエラーになったというときなどに、この記事がヒントになれば幸いです。

もっと詳しく知りたい場合は公式のリファレンスを参照してください。

12
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?