TypeScriptでバックエンドを書いてもいい時代です

に公開
5

下のようなツイートを見かけたので少し書いてみます。

https://x.com/nakasyou0/status/1926457184516288681

おそらくこの方はバックエンドをTypeScriptで書くのは良くないという誰かの意見に反応したものだと思う。そういう意見に惑わされないためにも、宣言しておきます。もうすでにTypeScriptでバックエンドを書いてもいい時代です、と。

っていうか書いてもいいどころか、すでにみんなTypeScriptでバックエンド書いて本番で運用してます。仕事上いろんな会社さんの開発の手伝いとかやってるんですが、全部TypeScriptでバックエンド書いてなくても、バックエンドの一部(BFFなど)をNext.jsやRemixのようなフレームワークを使って書いているところを含めるともう最近は見聞きする相手はみんな本番で運用してます。なのでもうTypeScriptでバックエンド書いてもいいです。

TypeScriptでバックエンド書くということは、多くの場合ランタイムとしてはNode.jsをバックエンドで動かすということになります(もしくはDenoやbun)。Node.jsが登場した頃はバックエンドを記述するのに非同期エラーの補足が難しいなどの問題はありましたが、Promiseが導入されることでそれは解決されました。これは結構昔の話で、すでに解決済みです。

もうみんなTypeScriptでバックエンド書いて本番環境で動かしてるしTypeScriptという言語起因で困ってるみたいな話も全然聞きません。もう何にも問題ないのでTypeScriptでバックエンド書いちゃってください。終わり。

Discussion

あいや - aiya000あいや - aiya000

フロントエンドとバックエンドをどちらもTypeScriptにすれば、モジュラーモノリスにして、共通モジュールを使い回せますし、とても適した時代だと僕も思います!
各バンドラーも優秀らしいので、モノレポにしても、フロントエンドはフロントエンドで使っているものだけバンドルしてくれるはず。
BFFも場合によっては、フロントエンドで薄いクッション層を持つ程度でよくて、用意しなくてもよくなりそう。

makoto-developermakoto-developer

何でもかんでもJavaScriptで作ら良ればいいってもんじゃないけどミッションクリティカルなシステム以外でこちら都合で自由に止められるシステムならおっしゃる通りTSで十分だと思います。

良い面もあれば悪い面もあって、実効速度が遅いし、脆弱性多いし、生産性はいいかと言われればそうでもないし(現場にどれだけ優秀なひとをかき集められるかによる)、必ずトレードオフはあります。

ゆうたゆうた

TypeScriptでバックエンドは絶対ダメとまでは言えないですが、TypeScriptはJavaScriptにトランスパイルする前提で考えられているので、強い静的型付け言語とは言えない所がバックエンドで採用しづらい点だと個人的には考えます。

また、JavaScriptの悪いところをそのまま引き継いでいる所も良くないです。
int, floatなどの区別がなくnumber型が主に使われている事、unsignedが存在しない事、複雑なオーバーロード関数しか書けない事もTypeScriptを避けたくなる理由の1つです。

バックエンドではほぼ必ず実装しなければいけないデータベースとの通信も成熟しきっていない事も挙げられます。(TypeScriptの言語自体の問題ではないですが)
例えば、Prismaで生のSQLを書くことを許容するのであれば問題になりづらいと思いますが、本当に許容しますか?
as unknown as T のような型情報を強制的に書き換える書き方をしないといけない場面もあります。

そもそもasを利用しなければいけない場面も多くあり、asの曖昧さも問題です。
バックエンドでは型をasで書き換えるのではなく、厳密に型を意識した上で変換する処理を実装する事が求められるはずです。

以下のプログラムも型エラーになりません。
型の曖昧さを本当に許していいのですか?
Brand Typeのようなものをわざわざ意識するぐらいならTypeScriptでバックエンド構築はそもそも避けるという案も出てきて当然かと思います。

interface Interface1 {
  test1: number
}

interface Interface2 {
  test1: number
  test2: string
}

const func = (arg: Interface1) => {
  // 実装省略
}

const interface2: Interface2 = { test1: 1, test2: "abc" }
func(interface2)

本質とは異なるただの偏見ですがフロント系の人たち(ReactでTypeScriptを主に扱っている人たちなど)は年齢が若く新しいものが正義と考えているフシがあり、深く考えずに飛びついている印象です。
バックエンドではご法度です。脆弱性などがあった場合、お客様のデータが流出してからでは遅いです。
コードを書いていく上で培われたメンタルモデルがそもそも異なるのでフロント系の人たちにバックエンドを任せたくないという気持ちがあります。

  • WordPressで何でも構築しようとするフロントエンジニアの人たちのコードを皆さんは見たことがありますか?
  • エラーについて調べた結果「バージョンアップすると治る」と書かれていたのでとりあえずパッケージをバージョンアップする事が許されると思いますか?
  • TypeScriptのようなあいまいな型を許容している言語を扱い切れるでしょうか?他の言語だと言語レベルで制約が付いているのでミスが発生しづらいです。あえてTypeScriptを採用しなくて良いのではないでしょうか。C++ではなくRustが採用されているのは何故かという事も通づる話ではないでしょうか。
rión_devopsrión_devops

堅牢性で言えば、そもそも型がない言語を使うよりはベターな選択肢かと思われます。
あげてもらった課題は実際の現場では他の言語と併用して使うことで補完されていることが多いようです。(TSバックエンドの一本で行くわけではなくJava、Go、Pythonなどと併用)

suhbasuhba

バックエンドの定義を何か勘違いされていませんか?
フロントから叩かれるWebAPIのみがバックエンドなわけではありません。まず言葉の定義をまともにしていただかないと議論にならないかと思うのですが。
私の意見としては 単なるWebAPI用途ではNode.js+TypeScript の生産性は高いのでフロントも触っているチームが扱うのであればとてもいいと思いますが、より大きな汎用的なバッグエンド用途においてTypeScript (Node.js) はとても向いているとは思えません。

ブラウザから近いプログラムであれば向いていますが、そこから離れれば離れるほど全く向いていないというのが事実だと思います。

Promiseが導入されることでそれは解決されました

導入はされていますが、いまだに標準ライブラリなどは依然としてasync/awaitが利用できないコールバックスタイルのものが非常に多いのですが、果たして完全に解決しているのでしょうか?
(promisify は根本的な解説にはなっていない認識)
(更にJSのなんでもthrowできるというとんでもない仕様のせいで、例外も非常に扱いづらい)