## はじめに [[TypeScript]]でよく聞かれる質問に、2021年5月時点で[[📕tadashi-aikawa]]の立場から回答したもの。私の[[TypeScript]]経験は6年。[[🦉tadashi-aikawaのスキルマップ#個人プロダクト多数|個人のOSSプロダクト]]は10程度、仕事でも10近いツールやプロダクトを開発してきた。 > [!attention] > あくまで一個人の意見に過ぎず、公式に展開されたベストプラクティスとかではないため過信はしないこと。まして、時代が変われば何がベストかは常に変わる。ただの1記録として見てもらいたい。 ## Vue x Nuxt開発のベストプラクティスを教えてほしい 要件によってベストは変わるため、唯一のベストプラクティスは存在しない。[[create-nuxt-app]]でプロジェクト作成する場合、[[SPA]]開発にオススメな選定は以下。 | カテゴリ | 技術 | 備考 | | --------------- | -------------- | ------------------------- | | [[パッケージマネージャー]] | [[npm]] | [[yarn]]でも可 | | 言語 | [[TypeScript]] | | | [[UIフレームワーク]] | [[Vuetify]] | [[レスポンシブ]]、[[IE11]]対応で多機能 | | [[リンター]] | [[ESLint]] | 管理コストも高いので本当に必要か検討したほうがいい | | [[フォーマッター]] | [[Prettier]] | 必須。設定もデフォルトが無難で平和。 | | [[ユニットテスト]] | [[Jest]] | | [[create-nuxt-app]]でプロジェクト作成後、別途取り入れる技術は以下。 | カテゴリ | 技術 | 備考 | | ------------- | -------------------------- | -------------------------------- | | [[E2Eテスト]] | [[Playwright]] | | | [[Vue]] | [[Composition API]] | [[composition-api plugin]]を使う | | [[Nuxt2]] | [[vuex-module-decorators]] | [[Vuex]]を型安全に扱える | [[Composition API]]は今までのAPIと使い勝手が大きく異なるため、はじめの障壁は高い。[[Vue2]]経験者なら[[Composition API#MOC]]が参考になる。[[React]]経験者ならすんなり入れると思う。 ## TypeScriptを学ぶ際に初心者がつまずきやすいポイントは? 以下のケースでつまづく人が多い。 1. [[JavaScript]] [[ES2015]]以降の知識が不足している 2. ブラウザや[[Node.js]]、[[Babel]]などWebに関するエコシステムの前提知識がない 3. 型のある言語を使ったことがない [[TypeScript]]の学習サイトは[[JavaScript]]の知識を前提としているため、**1の習得は必須**である。[[📚JavaScript Primer]]の以下は理解しておきたい。 - [第一部 すべて](https://jsprimer.net/basic/) - [第二部 > アプリケーション開発の準備](https://jsprimer.net/use-case/setup-local-env/) ## 抑えておくべき周辺知識は? #### とりあえず動かしたい場合 [[📚サバイバルTypeScript]]が参考になる。 #### ちゃんと理解したい場合 [[📒TypeScriptの学習ソース]]で自分にあった文献を選ぶといい。また、[[JavaScript]]の理解を深めたほうがいいため[[#TypeScriptを学ぶ際に初心者がつまずきやすいポイント]]も参考に。 ## チームでTypeScriptを採用するときに決めた方がいいことは? - [[tsconfig.json|tsconfig.jsonの設定]] - 新規プロジェクトの場合[[strict]]および関連する設定はデフォルト推奨 - 移行プロジェクトの場合はフェーズによって緩和したほうがいい - [[ESLint]]を使うかどうか and 設定 - メンテコストもかかるので話し合ったほうがいい - [[Prettier]]の設定 - デフォルト推奨 ## TypeScriptのメリットは? - メリット - **型がある** (これが90%以上) - 型やプロパティに関する不具合を**実行せずに**検知できる - 昔の自分が書いたコードを**実行せずに**理解できる - 他人の書いたコードを**実行せずに**理解できる - ドキュメントがなくてもライブラリのAPI仕様を理解できる - [[Stage 3]]の仕様に対応している - 近いうちに採用される[[JavaScript]]の仕様が[[Babel]]ナシで利用できる - デメリット - 学習コストがかかる - [[JavaScript]]をちゃんと理解できていれば学習コストは低い - 型を書くだけでPayできるはず - フレームワークやライブラリの導入が面倒になる可能性はある - READMEどおりに動かない可能性がある - 最近は[[TypeScript]]サポートが一般的になったので楽になってる ## バージョンアップは大変? [[TypeScript]]自体のバージョンアップは楽。 - 下位互換性が崩れることはほとんどない - 最新バージョンを使いたいけど[[Nuxt2]]などのフレームワークが対応していなくてバージョンアップできない..とかはある - メジャーなライブラリであればすぐ使えるようになる - 安定した頃に対応されるという捉え方をすると逆に都合がいい ## ライブラリを使うと型定義エラーになり面倒... 型定義ファイルが同梱されていたり、`@types/...`で提供されている場合、エラーになることはほとんどない。本当にそのようなことが起きているのか? 型定義ファイルが提供されていない場合に自分で型定義ファイルを作ることはあっても、別のライブラリに影響を与えることはないはず。 ## 型設計で考えるべきことは? - 知っていることを書く - 理由なき[[any型]]はNG - [[any型]]を使いたくなったら以下も検討する - [[unknown型]]で代用できないか - 自分で使い捨ての型を定義できないか - メリットがなければ高度な型表現を使う必要はない - [[条件付き型 (TypeScript)|条件付き型]]や[[可変長タプル型]]は大抵の場合使う必要はない - 使うとしたら - 複雑な仕様をもつ[[JavaScript]]ライブラリに型定義を提供するとき - 高度に型を解釈するライブラリを開発するとき - 逆によく使う型表現は - [[ユニオン型 (TypeScript)|ユニオン型]] - 特に`string`の[[ユニオン型 (TypeScript)|ユニオン型]] - [[オプショナルプロパティ]]であるかはちゃんと考える - はじめは[[オプショナルプロパティ]]にしない方が実装は楽 ## JavaScriptで書かれたプロダクトへ導入するメリットは? - メリット - 新しく書いたコードの開発速度/品質が上がる - デメリット - [[JavaScript]]部分と[[TypeScript]]部分の二重管理になる - すべて[[TypeScript]]ベースに移行すれば問題ない - [[TypeScriptはJavaScriptのスーパーセット]]であるため変換は不要 - ただし、はじめから[[strict]]オプションを有効にできない ## JavaScriptが染みついているチームでの導入方法は? - ちゃんと勉強する - 無理のない範囲で少しずつ導入する ## typeとinterfaceの違いは? > [!info] > #2025/10/26 にリンク先を最新化。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/favicon-64.png" /> <span class="link-card-v2-site-name">Minerva</span> </div> <div class="link-card-v2-title"> 📰TypeScriptにおける型エイリアスとインターフェースの違い </div> <div class="link-card-v2-content">TypeScriptの型エイリアスとインターフェースの違いや使い分け、extends・implements、同名定義、型の別名などの特徴を解説している。</div> <img class="link-card-v2-image" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/Notes/attachments/report.webp" /> <a data-href="📰TypeScriptにおける型エイリアスとインターフェースの違い" class="internal-link"></a> </div> %%[[📰TypeScriptにおける型エイリアスとインターフェースの違い]]%% ## Enumではなくユニオン型を使ったほうがいい理由は? ### [[Tree-shaking]]されないから 不要なコードが残ってしまう。詳細は[[📚TypeScriptのenumを使わないほうがいい理由を、Tree-shakingの観点で紹介します]]が参考になる。 ### [[as const]]で代用できるから ただし、[[TypeScriptのas constはTree-shakingされない]]ので注意。 ### [[Enum (TypeScript)|Enum]]は[[JavaScript]]の機能として実装されていないから [[JavaScript]]に[[Enum (TypeScript)|Enum]]が実装されたときに仕様が競合するリスクが小さい。 ### 個人的には... [[TypeScriptで多機能なEnumを実装する方法]]を使っている。[[Tree-shaking]]されないが[[Java]]のようにふるまいを抽象化できるため便利なので。 ## 読み取り専用にする方法 [[TypeScriptでas constを使えば明示的に記述した箇所は読み取り専用]]にできる。しかし、返却値が確定していない場合は書き込みできてしまう。 ```typescript import * as fs from "fs"; interface Package { name: string; } function loadPackage(): Package { return JSON.parse(fs.readFileSync("./package.json", { encoding: "utf-8" })); } const pkg = loadPackage(); const obj = { id: 1, body: pkg, } as const; // これはエラーにならない.... obj.body.name = "hoge"; console.log(obj.body); ``` その場合は[[Object.freeze]]を使うと実現できる。 ```typescript import * as fs from "fs"; interface Package { name: string; } function loadPackage(): Package { return JSON.parse(fs.readFileSync("./package.json", { encoding: "utf-8" })); } const pkg = Object.freeze(loadPackage()); const obj = { id: 1, body: pkg, }; // 書き込み不可エラーになる obj.body.name = "hoge"; ``` ### 個人的には.. [[読み取り専用プロパティ (TypeScript)|読み取り専用プロパティ]]も[[as const]]も[[Object.freeze]]も使わない[^1]。変更することのリスクをチームの文化として染みつかせることに注力した方が、コードの可読性も上がって生産的と感じるから。 [^1]: コアライブラリの実装など例外もある ## おすすめの記法 どちらも[[JavaScript]]の仕様であり、[[TypeScript]]の仕様である。 - [[オプショナルチェーン (JavaScript)|オプショナルチェーン]]と[[Null合体演算子 (JavaScript)|Null合体演算子]] - [[async function (JavaScript)|async function]] あとは[[型述語 (TypeScript)|型述語]]は覚えておくと便利なことが多い。 ## TypeScriptを使う大きなデメリット 大きなデメリットはない。 ## TypeScriptのバージョンを上げるタイミング ### フレームワークを使っている場合 依存しているフレームワークがバージョンアップしたとき。 フレームワークが依存する[[TypeScript]]のバージョンは、フレームワークの[[dependencies]]に記載されていることが多いため、あまり意識する必要はあまりない。たとえば[[Nuxt2]]の場合は[[@nuxt.typescript]]の[[dependencies]]に`typescript`と対応バージョンが記載されている。 ### フレームワークを使っていない場合 好きなタイミングで良い。Stableになっていれば不具合もほとんどない。 ## JSと比較したときどこがいいのか [[#TypeScriptのメリットは]]を参照。 ## アンチパターン **『コンパイルが通らなかったので`any`にしました』** よく聞く理由と反論。 - 『型が何か分からなかったので』 - 『型も分からなくて正しく動くモノが作れるんですか?』 - 『一部なら分かるけど全部は分からない』 - 『分かるところだけ型定義しましょう (全部書く必要はない)』 - 『面倒くさいので』 - 『関数の引数/戻り値、クラスのメンバーだけでいいはずなので書いてください。変数は型推論されるので記載は不要です』 - 『時間がないので』 - 『型を書かないから時間がなくなるんです』 - 『書かなくても分かるんで』 - 『じゃあ、△さんが1年前に書いた〇〇のコードがどう動くか説明してみてください。もちろんデバッグなしで』 [[型アサーション (TypeScript)|型アサーション]]は状況によって使わざるをえないケースがあるため、一概にアンチパターンとは言えない。また、真っ当な理由があれば[[any型]]で問題ないケースもある。 ## 最新のTypeScript事情は? [[TypeScript 3.7]]でリリースされた[[オプショナルチェーン (JavaScript)|オプショナルチェーン]]と[[Null合体演算子 (JavaScript)|Null合体演算子]]以降は、一般ユーザーにとってインパクトの大きい新機能はない。逆にライブラリやフレームワークなどを開発するコアユーザーには嬉しい機能が出ている。[[TypeScript 4.0]]と[[TypeScript 4.1]]の目玉機能を紹介する。 ### [[可変長タプル型]] 可変長の引数をもつ関数定義が楽になった。具体例は[[可変長タプル型#tail関数]]を参照。 ### [[テンプレートリテラル型]] 組み合わせパターンをもつstringの[[ユニオン型 (TypeScript)|ユニオン型]]が作りやすくなった。