Open9

【TS】記事にする程ではない豆知識

ShinyaHinoharaShinyaHinohara

現代のオブジェクト指向では継承よりも合成の方が推奨されている。

GoやRust では、実装を伴った継承がそもそも存在しない。
現代は継承そのものを避けるべきという認識が開発者の間に広まってきてるため。
React でもコンポーネントをクラスで作成するときは、継承を避けるよう公式ドキュメントに書かれてる。

アクセス修飾子を使いたくなったら、それはこういう避けるべき継承を前提として設計しようとしてるということ。どうしてもインスタンスからアク セスさせたくないものだけ private を使ってもいいかもしれない。

同じ理由で、abstract 修飾子を用いて抽象クラスを定義するのも微妙。
インターフェースを使うべき。

ShinyaHinoharaShinyaHinohara

TypeScriptはデフォルトの設定ではすべての型にnullとundefinedを代入できてしまう。
それが嫌ならstrictNullChecksを設定する必要がある。

nullを許容したい時は、共用体型で明示的に表現する。

ShinyaHinoharaShinyaHinohara

nullアクセスのエラー時には、プロパティアクセスの前に ! を書く記法がある。

非Null アサーション演算子と言い、『ここには絶対にnullもundefinedも入りませんよ』とコンパイラを強引に黙らせるもの。ただこれはせっかくの null 安全性を壊すもので、実際に値がnullやundefinedだったら実行時エラーになる。

だからよっぽどの保証がない限り使うべきじゃない。

ShinyaHinoharaShinyaHinohara

type DateFormat = ${number}-${number}-${number};
のように型定義ができる。

テンプレートリテラル型。

ShinyaHinoharaShinyaHinohara

TypeScriptのバージョンが低かったころは、インターフェースにできて型エイリアスにできないことがけっこうあったようです。

なので、古い記事ではインターフェースを優先的に使うことを推奨していたり、代表的な構文解析ツールの推奨設定でも新規の型定義にはインターフェースを使うようにするルールが有効になってたりします。

しかし、 TypeScript のバージョンが進むにつれ型エイリアスのできることが増えていき、ついにはその立場が逆転してしまいました。

そして、今となっては型エイリアスのほうがはるかに表現力が高く、 インターフェースを優先して使う理由がなくなっている。

ShinyaHinoharaShinyaHinohara

最後に条件付き型について説明していきます。

まず前提として、クラスやインターフェースの拡張に使われる extends キーワードは型引数の表現にも適用できます。


const override = <T, U extends T>(obj1: T, obj2: U): T & U => ({
...obj1,
...obj2,
});

ここでのextends は、関数 overrideの第 2 引数obj2の型を定義している型引数 U が第 1 引数の型 obj1 の型 T と同じか拡張したものでなければならなくなります。

そして、この性質を利用し、T extends U ? X : Yのように条件によって型を変えることができるのです。

また、マッチした型はinferを使え参照可能になります。

ShinyaHinoharaShinyaHinohara

TypeScript では、同じ名前空間の中に『変数宣言空間』と『型宣言空間』という 2 つの宣言空間が存在してい て、名前の管理が別々になっている。

同じ名前の変数と型がある時は一緒に、インポート・エクスポートされる。

型のみのインポートと型のみのエクスポートも可能。

ShinyaHinoharaShinyaHinohara

import bar from './bar';

をすると TypeScript は次の順にモジュールを探索していく。

  1. src/bar.ts
  2. src/bar.tsx
  3. src/bar.d.ts
  4. src/bar/package.json の types または typings プロパティで設定されている型定義ファイル
  5. src/bar/index.ts
  6. src/bar/index.tsx
  7. src/bar/index.d.ts

なので、定義ファイルのないnpmパッケージを使用したい時は、hoge.d.tsを作って型定義を追加すれば良い。

ShinyaHinoharaShinyaHinohara

strictというオプションはちょっと特殊なオプションで、これを有効にすると次のオプションたちがまとめて有効にされる。

・noImplicitAny …… 暗黙的に any が指定されている式や宣言があればエラーになる
・noImplicitThis …… this が暗黙的に any を表現していればエラーになる
・alwaysStrict …… すべてのソースファイルの先頭に 'use strict' が記述されているものとみなす
・strictBindCallApply …… bind()、call()、apply() メソッド使用時に、その関数に渡される引数の型チェックを行う
・strictNullChecks …… 他のすべての型から null および undefined が代入不可になる
・strictFunctionTypes …… 関数の引数の型チェックが「共変的」ではなく、「反変的」に行われるようになる
・strictPropertyInitialization …… 宣言だけで初期化されないクラスプロパティ(=メンバー変数)があるとエラーになる