エンターテイメント!!

遊戯王好きのJavaエンジニアのブログ。バーニングソウルを会得する特訓中。

【Typescript移行】完結編 ~そして静的型の世界へ~

経緯

js→tsへの移行がやっと終わって、mainブランチに統合できたから、これまでの経緯を振り返って見ようと思い書いた。

移行話

とりあえず、移行を思い立ってから、移行が完結するまでを物語風でまとめておく。

内容

一章 決意

最初の段階で、tsにするかjsにするか検討することはあった。
当初は、typescriptで開発しようと思い、開発環境を調べた。
しかし、調べれば調べるほど、あれもしたい、これもしたいが積み重なり、本当にしたい開発が後手後手になっていることに、環境調査沼にハマってから気づいた。。。

特にハマっていたのは、webpack関連のビルドと、デザイン周りのモック開発ツールの選定&モックの作成。

もう、初速が使いので、typescriptに見切りをつけて、javascriptで実装を強行。
jsで実装するにあたり、気を使ったのは、デザイン周りとelectronのセキュリティ関連の実装だった。
デザインは、前の段階である程度形になっていたので、それほど問題ではなかったが、bootstrapやjqueryで悩むことが多かった。webの情報をそのまま鵜呑みにすると、デザイン崩れたりして、嫌気が刺しやすいから、なるべくシンプルなものを作ることを心がけた。
npmやgitは、仕事で使った経験があったので苦労はしなかったが、経験がなかったら、もっと苦労してたかもしれない。。。
electronで一番苦労したのは、preloadの概念。
利用したことがなかったので、学習コストがかかったのが辛かった。

とりあえず、rss情報を収集する機能は、速攻で実装できた。
デザインを思ったとおりに作って、動きも合わせて実装できたときの快感は素晴らしい。
たぶん、ドーパミンがどぴゅどぴゅ出てたと思う。

modal機能を実装するときにbootstrapやjqueryのことを色々調べたのが辛かったくらいで、比較的順調に実装はできてた。
ただ、ipc通信部分を作り込んでいるときは、型情報が欲しいと思った。
それと同時に、型を導入したら、preloadのところで苦労するだろうなぁ~とは感じていた。

開発を勧めていくうち、この引数なんだっけ?から始まり、引数の内容を調査し始めると、途中の処理が気になり、最初にやっていた作業を忘れ、別の作業をやり始めてしまう自体に。。。
そして、元のことを忘れたままということに。。。

いろいろ考えたが、静的型付は必要という結論に至った。
理由としては、自分の特性によるものが大きかった。
忘れっぽく、完璧主義の思想があったため、前述したように引数の設定内容調べ始めたときに、気に食わない実装を見つけると、それに注力してしまっていた。そのため、優先順位を無視して気の向くまま作業してしまい、なかなか成果が上がらなかった。
型を導入することで、変な方向に気が向く機会を減らし、やるべき作業に集中できるようにしたほうがメリットデカいと感じたため、js→typescriptへの移行を決意した。
また、本職はJavaエンジニアということもあり、デザインパターンにもある程度精通していたため、静的型付の方が開発効率が上がる気がしたのも、移行を決めた要因の一つではある。

二章 挫折

移行を決意して、js→typescriptへの移行作業を実施。
typescript自体は、業務で触ったことがあるので、tsconfigの準備やら環境準備、webpackのビルド環境用意は、比較的順調にできた。
目的が移行だったので、環境調査に気が向くことはなかった。

jsファイルの拡張子を一括でtsに変えてビルドエラーを潰してイケば、移行できるだろうと思っていたが、それが挫折へと向かう要因であった。。。。

tsに変換して、初回ビルドした結果、100件オーバーのエラーが。。。
tsconfigで条件を緩くしても、エラーの数はあんまり変わらなかった。。。
型定義ファイルを用意したり、ビルドエラーを一つずつ消して行く作業で精神が摩耗していき、なんとか全部解決する頃には、かなりイライラしている状態になった。
そして、いざ実行してみたら、今度は、electron周りでエラーが。。。

たぶん、mainとrendererを別個に出力してなかったのが問題だったと思うが、当時はその発想がなく、削りきられた精神状態にelectornの実行エラーがトドメとなり、もこう先生並の台パン決めて移行を諦めた。。。

electronのエラー表示が不親切ってのも、問題としてあると思う。。。

三章 再挑戦

諦めてjsで機能実装をしていくが、どうしても性格的な問題で気がそれるのが治らず、イライラすることが多かった。。
スラムダンクの三井並に、モニターに向かって「typescriptに移行したいです」という本音が出てきたので、楽して移行できないか調べてから再挑戦することに。

いろいろ調べた結果、ts-migrateを見つけることができた。
前回は、js→tsでかなり摩耗したから、それをすっ飛ばして他の問題に注力できるのでは無いかと思い、再挑戦。

前回はコード変換で苦戦したが、ts-migrateである程度、機械的に変換できたので、他の問題に注力できる余力が生まれた。
preload使っているので、型情報ファイルが必要になったが、別の問題がでてくるのが目に見えてたので、とりあえずビール的なノリで、とりあえずanyで誤魔化して済ませておいた。
前回は、考える前に辞めてしまったが、webpack絡みで問題があったことに気づき、いろいろ四苦八苦しながら対応して、なんとか移行を完了できた。
型定義ファイルの作成にも苦労したが、どちらかというと、webpackが一番難所だった。
webpack職人と言われる人たちが出てくる闇を垣間見た気がする。。。
理屈が分かれば、対応できるのだが、裏の構造が分かりづらいので、トライ・アンド・エラーになってしまい、職人ができてしまうんだろうなと思う。

とりあえず、移行が成功できて素直に嬉しい。
ts-migrateが特に役立った。
別の問題に注力できる心の余裕ができたのが、移行ができた要因だと思う。
チーム開発なら誰かに頼るとかできるけど、個人開発だとこういうのが辛い。。。
ツイッターで適当に呟いておいて、ヘルプ求めておけば良かったのかもしれないと、後になって思ったが、知らない人からリプライきたら、ビビって無視してしまいそうな気がしないでもない。

思ったこと・学んだこと

typescript移行作業

  • jsからの移行は、ソースを変えるだけでは無理
    • ビルド環境の構築、周辺環境の理解が深くないと、問題がいろいろ絡みあって挫折する
    • webpack等のビルドツールが必須になるので、typescript以外にもwebpack等の知識が必須
    • 移行で楽できる箇所は楽をする
      • 別の問題に注力できる余力を残しておいたほうがいい
      • 楽をすることは悪ではない。※無知は悪だと思うけど、知ってる上で楽するのは全然OK
      • 移行するまでどんな問題が出てくるのか分からないので、心の準備はしておく
  • なぜ移行したいのか、目的は明確にする
  • ストレスでハゲるかもしれない覚悟はする
  • 心の余裕、超大事

移行したメリット

開発勧めて行く上で分かってくると思うので、記述は辞めておく

その他雑記

書いていて思ったが、俺ってメンタル弱すぎでは?って思った。。。
そこそこやるエンジニアだと思っているが、心の余裕が無いと、実力あっても諦めるんだなって感じた。
最近、メンタル不調から復帰して来たからかもしれないが、心の余裕って、パフォーマンスに大きく影響するものだと思った。ここらへん、アスリートみたいだなと感じた。

過去記事

suzaku-tec.hatenadiary.jp

suzaku-tec.hatenadiary.jp

suzaku-tec.hatenadiary.jp

suzaku-tec.hatenadiary.jp

【typescript移行】Typescriptで`'x' is not defined`

経緯

js→tsへの移行をしているのだが、掲題の通り'x' is not definedが発生していて、かなり悩んだ。。。

ts移行の記事は以下

suzaku-tec.hatenadiary.jp

suzaku-tec.hatenadiary.jp

suzaku-tec.hatenadiary.jp

内容

htmlからtsで定義したfunctionを呼び出しているのだが、呼び出せていない。

ソース

ts側

function showSettingModal() {
・・・
}

html側

<a class="dropdown-item" href="#" id="show message" onclick="showSettingModal()" >show text modal</a>

原因

typescriptに移行した実装は、即時実行関数でラッピングされるため、そのまま使おうとすると、参照スコープ外になり、参照できないため、'x' is not definedが発生していた。。。

昔、typescriptやってた時も、そんなことあったなぁ~と思いながら、台パンした。

jsで動いていたソースで、変化がなかったので、問題なく動くはずだと思っていたが、ビルド結果で差異が出てくるの、マジできつい。。。

対応内容

window.showSettingModal = function showSettingModal() {
・・・
}

とりあえずwindowsに適当に加えたら動いた。
型定義で警告出てるから、その対応が必要だけど、とりあえず動かないからは前進できた。。。

参考サイト

javascriptのバンドルツールで詰まった | Octo's blog

雑記

最近、無性にキレやすくなってる気がする。。。
なんとかせねば。。。

Typescriptで`'x' is not constructer`

経緯

js→tsへの移行で実行時に'x' is not constructerというエラーが出てきた。
コンストラクタは宣言してるはずなのになぁ~って思って調べていたが、かなり沼にハマったので、記録を残しておく。

なお、過去の移行話は、下記に散らばってるので、興味があれば。

suzaku-tec.hatenadiary.jp

suzaku-tec.hatenadiary.jp

原因

理由は、そのままコンストラクタがないから。
たしかに、consoleデバックすると、constructorが存在してない。

対応

下記の対応をしたら動いた。

import * as RsshipTab from "./RsshipTab";

import RsshipTab from "./RsshipTab";

ちなみに、RsshipTabの概要は以下。

export default class RsshipTab {
・・・・
}

あんまり詳しいこと書いてあるサイトがなかったので、予想だが、別名付けた際に、同じ名前で別名付けてしまったときにコンストラクタが消失したのだと思っている。
別名の仕組みが分かってないゆえに起きた事象だと思う。。。
※今でもよく分かってないが。。。

参考サイト

javascript - Typescript es6 import module "File is not a module error" - Stack Overflow

いろいろ調べたんだけど、欲しい情報と違う情報ばっかり出てきて、かなり難航した。。。
ググる能力がないのかとも考えるほどには悩んだ。
「ググれカス」ならぬ、「ググってもカス」なのか?って感じてしまった。。。
精神的に追い詰めるから、そういう言葉は言うもんじゃないなと思いました(小並感)

所感・感想・雑記

消失って言うと、どうしても「涼宮ハルヒの消失」を連想してしまうな。。。

どこで消失したのか探すのに苦労した。。。
理由と対処は、なんとなくサイトを横断して見ていったときに、もしやレベルで試したことが当たっていて、

ts-migrateが正常に終わっていると思っていたが、実は解決できてない箇所がFIXMEとかで残っていることに、最近ようやっと気づいた。
宣言うんぬんが一番多い気がする。。。
とりあえず、動作確認が終わったら、一個ずつ潰していこうと思っているが、辛たん。。。

js→typescript移行で実行時に`Electron failed to install correctly, please delete node_modules/electron and try installing again`

経緯

下記の記事でts→js移行したのだが、ビルドはできた。
しかし、実行時にエラーが。。。

suzaku-tec.hatenadiary.jp

エラー内容

App threw an error during load
Error: Electron failed to install correctly, please delete node_modules/electron and try installing again
    at getElectronPath (webpack://rsship/./node_modules/electron/index.js?:14:11)
    at eval (webpack://rsship/./node_modules/electron/index.js?:18:18)
    at Object../node_modules/electron/index.js (E:\dev\js\rsship\dist\main.js:1337:1)
    at __webpack_require__ (E:\dev\js\rsship\dist\main.js:1700:42)
    at eval (webpack://rsship/./src/main.ts?:3:10)
    at Object../src/main.ts (E:\dev\js\rsship\dist\main.js:1523:1)
    at __webpack_require__ (E:\dev\js\rsship\dist\main.js:1700:42)
    at E:\dev\js\rsship\dist\main.js:1727:37
    at E:\dev\js\rsship\dist\main.js:1730:12
    at webpackUniversalModuleDefinition (E:\dev\js\rsship\dist\main.js:11:20)

対応方法

webpack.config.jsのtarget: "node"target: "electron-renderer"に変更したら動作した。

targetは、特定の環境用にwebpackがいい感じでビルドするために必要らしい。
今回は、nodeを指定していたため、electron用のビルドになっておらず、実行時にエラーになった。
electron-rendererを指定することで、electron用のビルドをしてくれるようになり、ちゃんと実行できるようになった。

参考サイト

electron always "Electron failed to install correctly, please delete node_modules/electron and try installing again" · Issue #20731 · electron/electron · GitHub

webpack.config.js の書き方をしっかり理解しよう - Qiita

感想・雑記

webpack、未だに設定周りが理解できない。
webpack職人ができるのも頷ける。

今回の件で、targetの指定を間違えるとどういうことになるのかは理解できた。
コピペだけ繰り返していたから、こういう目に合うんだろうなぁ~とは感じた。

とりあえず、typescriptに移行できたのは、素直に嬉しい。
js→tsへの変換で四苦八苦してた頃を思い返すと、かなり楽に移行できたなと思う。

JavascriptからTypesctiptへの移行~ts-migrateを活用

経緯

個人で開発してるプロジェクトで、どうしても型を使えたほうが開発効率が上がりそうというのが見えてきたので、一旦挫折したTypescriptへの移行を四苦八苦しながら調査した

対応方法

いろいろ調べた結果、どうやらts-migrateを使えば、比較的、低労力で移行できそうなところまでは見えたので、ts-migrateを利用した。
ただ、調べた感じ、webpackが鬼門になりそうな感じがした。
前回は、typescriptへの置換で苦労して挫折したが、今度は、その先のビルド周りで苦労しそう。。。

手順

  1. ts-migrateのインストール
  2. ts-migrateの実行
  3. webpackのインストール
  4. webpackにてビルド

ts-migrateのインストール

npm i ts-migrate

ts-migrateの実行

npx ts-migrate-full src

webpackのインストール

npm i webpack webpack-cli ts-loader

インストールが終わったら、webpack.config.jsを作る。

プロジェクトによるので、一概にこうしろとは言えないので、自分のところのサンプルを晒す

module.exports = {
  entry: {
    'main': './src/main.ts',
    'renderer': './src/renderer.ts'
  },
  module: {
    rules: [
      {
        // ts-loaderでトランスパイルする
        test: /\.ts$/,
        exclude: /node_modules/,
        use: [
          {
            loader: "ts-loader",
            options: {
              transpileOnly: true,
            },
          },
        ]
      },
    ],
  },
  resolve: {
    modules: [`${__dirname}/src`, 'node_modules'],
    extensions: [".ts", ".js"],
    fallback: {
      "path": require.resolve("path-browserify"),
      "util": require.resolve("util/"),
      "crypto": require.resolve("crypto-browserify"),
      "buffer": require.resolve("buffer/"),
      "stream": require.resolve("stream-browserify"),
      "assert": require.resolve("assert/"),
      "os": require.resolve("os-browserify/browser")
    }
  },
  output: {
    libraryTarget: "umd",
    filename: "[name].js",
    path: `${__dirname}/dist`,
  },
  target: 'electron-renderer',
};

最低でも、entryとmodule.rules.useにts-loader、targetの指定が必要なはず。

webpackにてビルド

webpackを実行する。
自分は、npm scriptに "build-dev": "webpack --mode=development"を指定して実行している。

自分の環境では、以下のエラーメッセージがよくでてきてた。

If you want to include a polyfill, you need to:
        - add a fallback 'resolve.fallback: { "stream": require.resolve("stream-browserify") }'
        - install 'stream-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
        resolve.fallback: { "stream": false }

自分の場合は、一個一個fallbackに指定して、インストールして潰していった。
エラーメッセージの対処法に従っただけなので、内容はよく分かってない。。。

感想

とりあえず、ビルドするところまではできた。
実行でまだ問題が出てきそうな気がする。。。

参考サイト

ts-migrate:大規模にTypeScriptに移行するためのツール

JSからTSへの移行ツール、ts-migrateを試してみた - Qiita

"Cannot use 'in' operator to search for " が出て迷った

事象

jsonをparseして、プロパティの存在チェックにinを利用したときに、表題のCannot use 'in' operator to search for ...というエラー文言が出てきた。

エラーの内容

参考サイトからの引用

JavaScript の例外 "right-hand side of 'in' should be an object" は、 in 演算子が文字列、数値、その他のプリミティブ型の中を検索するために使用された場合に発生します。

つまり、inを利用しようとしたクラスがオブジェクトではなく文字列とか数値になっているらしい。

原因

jsonをparseしたと思っていたが、どうやらただの文字列になっていたらしく、変換に失敗した。
その場合は、parse時に SyntaxError が出て欲しかったのだが、出ないパターンがあるらしい。。。

インプットのデータを直して試したら、問題なく動作した。。。。

参考サイト

TypeError: cannot use 'in' operator to search for 'x' in 'y' - JavaScript | MDN

2021/07/05週 気づきと振り返り 夏場は出社したくない!

業務こなしての問題・気づき

c#

string.format内での中括弧表示

結論から言うと、{{って定義すれば、{って表示できる。
JSON形式の項目をログにするときに、必要になった。

Format関数の出力で中かっこ "{" "}" を表示する - C#プログラミング

普段のケースでは必要になることがほとんどないって、c#の開発者が言ってた。
JavaJavaScriptだと、そういうケースは多いと思うが、言語特性的なものだろうか?

レビュー

初期化は適切なタイミングで

初期化タイミングを無視して適当に初期化するのは、絶対にダメ。

実際のコードは晒せないので、Javaで似たような例を上げると、以下。

public class Test {
  public static void main(String args[]) {
    String str = "";
    try {
      str = "test";
    } catch(Exception e) {
      str.length();
    }
  }
}

空文字での初期化は、やるべきではない。
処理をリファクタリングしたりすると、本当の初期化の前にアクセスするコードとか作ってしまったたときに、気づいたときのテンションダウンが半端ない。

基本的に定義だけなら、null初期化が安全だと思う。
Javaだと、Optional使った方がいいのかな?

軽く流してしまったが、初期化は、少し気をつけてレビューしたほうがいいと思った。
リファクタリングが発生したときに、改修しやすいように心がけるべきだった。

その他雑記

今年は、梅雨明け遅すぎない?
人間の生産活動が、実は梅雨明けを早くしていた説ありそう。
感覚的な話なので、実際にそうかは分からないが。

去年は、マスク着用必須で通勤だったから、この時期はキツかった。。。
今年は、在宅で日射病にはならなさそうだけど、再出勤の話が出てるから、とてもナイーブだ。。。