令和5年に知っているべきTypeScriptのnamespaceの知識
TypeScriptにはnamespace
という構文が存在します。この構文はTypeScript初期からある独自構文の一つですが、現在では特殊な用途以外では使う理由が無いため、よく知らないという方も多いでしょう。
実際、一部のレアケースを除いてnamespace
を使う必要はありませんが、それでも知識としてあったほうが良いことが多少あります。この記事ではこの部分を解説します。
.
でアクセスできるやつ
型にTypeScriptを使っていると.
を使って型にアクセスする機会があるでしょう。例えばReact.FC
などです。
export const Page: React.FC = () => // ...
実は、親.型名
のように.
を使って型にアクセスできるのは、namespaceの機能です。上のコードでのReact
は単なる型や単なる変数ではなくnamespaceなのです。
試しに、Foo.Bar
がstring
となるようにFoo
を定義してみてください。これができる方法は2つしかありません。そのうちの1つがnamespace
です。次のようにFoo
を定義すればできます。
namespace Foo {
export type Bar = string;
}
このように関連する型をまとめて提供したい場合はnamespace
を使うことができます。この構文の中では、まるでモジュールのようにexport
を使うことができます(実際、namespace
は昔はmodule
という構文でした)。
namespaceを発生させるもう一つの方法
もう一つ、namespace
構文を使わずにFoo.Bar
を定義する方法があります。それはimport *
構文を使用することです。
export type Bar = string;
import type * as Foo from "./Foo";
// これでFoo.Barがstringとなる
ECMAScript仕様ではimport *
構文によって作られる変数(上の例のFoo
)をモジュール名前空間オブジェクト (module namespace object) と呼びます。この変数はTypeScriptの型システム上ではnamespaceとして扱われるので、Foo.Bar
のように.
で中の型にアクセスできます。
こちらを前提に置くと、namespace
というのは1つのファイルの中でネストしたモジュールを定義するものに見えてきます。実際、現在ECMAScriptにもネストしたモジュール定義を導入したいという動きもあるので、TypeScriptは期せずして10年くらい時代を先取りしていたことになりますね。
まとめ
TypeScriptでnamespaceを発生させる方法は、明示的にnamespace
構文を使う方法と、import *
構文を使う方法があります。
namespaceは、Foo.Bar
のように.
でアクセスできる型を提供する場合に有用です。ただ、アプリケーションコードであればimport *
で十分なのでnamespace
構文を使う機会はないでしょう。ライブラリの型定義を書く場合にもしかしたらnamespace
を使いたくなるかもしれません。
他の用途では使わなくていいです。
Discussion
これ便利ですね。紹介ありがとうございます。
定数定義のハンドリングでも便利かなと思い、少しデモ作ってみました。
定義側
使用側
簡単ですが、以上です。
ありがとうございます。しかし、定数を定義する目的で
namespace
を使うのは自分としてはあまり推奨しません。🥲理由は、わざわざ
namespace
という独自構文を使わなくても、普通のオブジェクトを使えばできるからです。記事中で「マジで使わないので今回は紹介しません」と書いたものです。Foo.Bar
のようにアクセスできる型をTypeScriptで実現するためにnamespaceの概念が必要になるため、そこだけこの記事で紹介しました。なるほどです。
namespace
の使い分けの基準が得られました。お返事ありがとうございました。