1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Babelでnamespace、moduleをトランスパイルする

Last updated at Posted at 2019-08-22

TL;DR

BabelでTypescriptのnamespace、module構文を扱う場合、.babelrcallowNamespacesを有効にする

.babelrc
{
    "plugins": [
        [
            "@babel/plugin-transform-typescript",
            {
                "allowNamespaces": true
            }
        ],
    ]
}

概要について

Next.jsでアプリを開発していて、TypeScriptのnamespace構文を使ってみた所、詰まったので解決策を残しておきます。

事象について

下記ディレクトリ構造、ソース、コンパイル時のエラー文です。

C:.
|   .gitignore
|   next-env.d.ts
|   package-lock.json
|   package.json
|   README.md
|   tsconfig.json
|
+---components
|       Layout.tsx
|       List.tsx
|       ListDetail.tsx
|       ListItem.tsx
|
+---interfaces
|       index.ts
|
+---pages
|       about.tsx
|       detail.tsx
|       index.tsx
|       initial-props.tsx
|
\---utils
        index.ts
        sample-api.ts

X.method()を呼び出す、X.method()は文字列を返す

pages/index.tsx
import * as React from 'react'
import Link from 'next/link'
import Layout from '../components/Layout'
import { NextPage } from 'next'
import { X } from '../utils/index'

const IndexPage: NextPage = () => {
  return (
    <Layout title="Home | Next.js + TypeScript Example">
      <h1>Hello Next.js 👋</h1>
      <p>
        {X.method()}
      </p>
      <p>
        <Link href="/about">
          <a>About</a>
        </Link>
      </p>
    </Layout>
  )
}

export default IndexPage

X.method()hello文字列を返す

util/index.ts
export namespace X {
    export const method = (): string => {
        return "Hello"
    }
}

nextコマンドでビルドサーバ起動時にコンパイルエラー

[ error ] ./utils/index.ts
SyntaxError: ~\nextapp\utils\index.ts: Namespace not marked type-only declare. Non-declarative namespaces are only supported experimentally in Babel.
To enable and review caveats see: https://Babeljs.io/docs/en/Babel-plugin-transform-typescript
> 1 | export namespace X {
    |                  ^
  2 |     export const method = (): string => {
  3 |         return "Hello"
  4 |     }

Next.js, Babel, TypeScriptについて

上記のエラーを読みとく前に、Next.js、Babel、TypeScriptの関係について簡単にまとめてみたいと思います。

  • Next.js

    Next.jsはIsomophic JavaScriptと呼ばれるSPAとSSRを実現できるフレームワークです。
    クライアントとサーバをJavaScriptで記述でき、ユーザビリティも高いです。

    Isomophic JavaScriptについてはこの記事で分かりやすく記述されています。

    Next.jsのバージョン9が2019年8月8日にリリースされました

    Next.js 9ではTypeScriptをビルトインサポートしています、TypeScriptを利用した場合でも、webpack.config.jsnext.config.js等の設定は要りません。

    そしてNext.jsではバンドラにWebpackを利用していて、loaderにはデフォルトではBabelを利用しています。

    なので、Next.jsはts,tsxファイルをBabelを利用してバンドルします。

  • TypeScript

    TypeScriptはaltJSと呼ばれる代替JavaScript言語です。
    JavaScriptは動的型付け言語なので型を記述出来ませんが、TypeScriptを利用するとJavaScriptを静的型付けで記述出来ます。

    型を宣言、指定出来る他、クラス、継承、インターフェース、ジェネリクス、名前空間などC++、Javaなどのオブジェクト指向言語で実装されている機能を利用できます。

    今回詰まったのは名前空間ですね。

  • Babel

    トランスパイラと呼ばれるJavaScript構文変換を行うコンパイラです。
    ES6以降の次世代のJavaScriptを記述し、ソースをBabelでES5に変換することで主要なブラウザで動作できるというものです。

    余談ですが、最近ではF#のパイプ構文が追加されたようです、Bashのパイプが好きだったので嬉しいです、直接関係はありませんが。

    Babelのバージョン7.5.0が2019年7月4日にリリースされました。

    Babel 7.5.0ではTypeScriptのnamespace構文が試験的にサポートされています。

    そしてNext.js 9ではparser等にBabel 7.5.0を使用しています。

まとめると、Next.js 9はBabel 7.5.0を使用していてBabel 7.5.0はTypeScriptのnamespace構文を試験的にサポートしているという事です。

TypeScriptを使ったことがなかったので、てっきりTypeScriptの構文はBabelによって完全にサポートされているかと思いましたが現状試験的なサポートの様です。

事象の解決

先に挙げたエラー文ではNamespace not marked type-only declare. Non-declarative namespaces are only supported experimentally in Babel.と書かれています。

これはBabelではdeclareで宣言されていないnamespaceをサポートしないと解釈できます。

検証のためにBabel 7.5の環境を構築してみます、適当な空のディレクトリに移動して、package.jsonを作成し、下記をコピペし、npm iコマンドを実行してください。

package.json
{
  "name": "babel-namespace-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "type-check": "tsc --noEmit",
    "build": "babel index.ts --out-dir dist --extensions \".ts,.tsx\" --source-maps inline",
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@babel/cli": "^7.5.5",
    "@babel/core": "^7.5.5",
    "@babel/plugin-proposal-class-properties": "^7.5.5",
    "@babel/plugin-proposal-numeric-separator": "^7.2.0",
    "@babel/plugin-proposal-object-rest-spread": "^7.5.5",
    "@babel/preset-env": "^7.5.5",
    "@babel/preset-typescript": "^7.3.3",
    "typescript": "^3.5.3"
  }
}

また.babelrcを作成し、下記をコピペしてください。

.babelrc
{
    "presets": [
        "@babel/env",
        "@babel/typescript"
    ],
    "plugins": [
        "@babel/proposal-class-properties",
        "@babel/proposal-object-rest-spread",
        [
            "@babel/plugin-transform-typescript",
            {
                "allowNamespaces": false
            }
        ],
    ]
}

index.tsを作成し、下記をコピペし、npm run buildコマンドを実行するとエラー発生しません。

index.ts
// 抽象的な名前空間を宣言
declare namespace X {
    export type method = () => string
}

// インターフェースに従って実装
const method: X.method = () => 'Hello'

console.log(method())

しかしindex.tsに下記をコピペし、npm run buildコマンドを実行するとエラーが発生します。

index.ts
// 実装された名前空間を宣言
namespace X {
    export const method = () => `Hello`
}

console.log(X.method())

これはBabelがdeclareで宣言された名前空間のみをサポートすることを意味します。

例えばnpm run type-checkを実行した場合、上記の例はどちらとも通ります。
つまりTypeScriptの観点では構文自体は合っていると解釈できます。

エラー文にはTo enable and review caveats see: https://Babeljs.io/docs/en/Babel-plugin-transform-typescript、つまり実装的な名前空間を有効にする方法はURLを見てね.と書いてあるのでURLを確認すると確かに書いてありました。

namespacess not marked with declare are experimental and disabled by default. Not enabling will result in an error: "Namespace not marked type-only declare. Non-declarative namespaces are only supported experimentally in Babel."

Workaround: Enable the allowNamespaces option.

allowNamespacesオプション付けることで回避出来る書いてあります。

余談ですがtsconfig.jsonにオプションを記述してエラーになりました。
namespaceをトランスパイルするのはBabelなので.babelrcに記述するのが正解ですね。

なので、.babelrcを下記の様な状態にすることで、下記のindex.tsはトランスパイル出来ます。
npm run build && node dist/index.jsコマンドが成功するはずです。

.babelrc
{
    "presets": [
        "@babel/env",
        "@babel/typescript"
    ],
    "plugins": [
        "@babel/proposal-class-properties",
        "@babel/proposal-object-rest-spread",
        [
            "@babel/plugin-transform-typescript",
            {
                "allowNamespaces": true
            }
        ],
    ]
}
index.ts
namespace X {
    export const method = () => `Hello`
}

console.log(X.method())
1
1
1

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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?