基本的な型
Number
、String
、Boolean
、Symbol
、およびObject
❌ **してはいけないこと**: Number
、String
、Boolean
、Symbol
、またはObject
型を決して使用しないでください。これらの型は、JavaScriptコードで適切に使用されることはほとんどないプリミティブ以外のボックス化されたオブジェクトを参照しています。
ts
/* WRONG */function reverse(s: String): String;
✅ **すべきこと**: number
、string
、boolean
、およびsymbol
型を使用してください。
ts
/* OK */function reverse(s: string): string;
Object
の代わりに、プリミティブ以外のobject
型を使用してください(TypeScript 2.2で追加)。
ジェネリクス
❌ **してはいけないこと**: 型パラメーターを使用しないジェネリック型を決して作成しないでください。TypeScriptのFAQページで詳細をご覧ください。
any
❌ **してはいけないこと**: JavaScriptプロジェクトをTypeScriptに移行中ではない限り、any
を型として使用しないでください。コンパイラはany
を「この部分の型チェックをオフにしてください」として*事実上*扱います。これは、変数のすべての使用箇所に@ts-ignore
コメントを付けるようなものです。これは、最初にJavaScriptプロジェクトをTypeScriptに移行する場合、まだ移行していないものの型をany
として設定できるため非常に役立ちますが、完全なTypeScriptプロジェクトでは、これを使用するプログラムの任意の部分の型チェックが無効になります。
受け入れる型がわからない場合、または何もせずに盲目的に受け渡すため、何でも受け入れる必要がある場合は、unknown
を使用できます。
コールバック型
コールバックの戻り値
❌ しないでください: 値が無視されるコールバックに`any`を戻り値型として使用しないでください。
ts
/* WRONG */function fn(x: () => any) {x();}
✅ 行ってください: 値が無視されるコールバックには`void`を戻り値型として使用してください。
ts
/* OK */function fn(x: () => void) {x();}
❔ 理由: `void`を使用すると、`x`の戻り値を意図せずチェックせずに使用してしまうことを防ぐため、より安全です。
ts
function fn(x: () => void) {var k = x(); // oops! meant to do something elsek.doSomething(); // error, but would be OK if the return type had been 'any'}
コールバックにおけるオプションパラメーター
❌ しないでください: 本当に必要でない限り、コールバックでオプションパラメーターを使用しないでください。
ts
/* WRONG */interface Fetcher {getObject(done: (data: unknown, elapsedTime?: number) => void): void;}
これは非常に具体的な意味を持ちます。`done`コールバックは、1つの引数で呼び出されるか、2つの引数で呼び出される可能性があります。著者は、コールバックが`elapsedTime`パラメーターを気にしない可能性があると意図していた可能性がありますが、これを実現するためにパラメーターをオプションにする必要はありません。より少ない引数を受け入れるコールバックを提供することは常に合法です。
✅ 行ってください: コールバックパラメーターを必須として記述してください。
ts
/* OK */interface Fetcher {getObject(done: (data: unknown, elapsedTime: number) => void): void;}
オーバーロードとコールバック
❌ しないでください: コールバックの引数の数だけが異なるオーバーロードを別々に記述しないでください。
ts
/* WRONG */declare function beforeAll(action: () => void, timeout?: number): void;declare function beforeAll(action: (done: DoneFn) => void,timeout?: number): void;
✅ 行ってください: 最大の引数の数を用いて、単一のオーバーロードを記述してください。
ts
/* OK */declare function beforeAll(action: (done: DoneFn) => void,timeout?: number): void;
❔ 理由: コールバックがパラメーターを無視することは常に合法であるため、より短いオーバーロードは必要ありません。より短いコールバックを最初に提供すると、最初のオーバーロードと一致するため、型が間違った関数が渡される可能性があります。
関数オーバーロード
順序
❌ しないでください: より一般的なオーバーロードを、より具体的なオーバーロードの前に置かないでください。
ts
/* WRONG */declare function fn(x: unknown): unknown;declare function fn(x: HTMLElement): number;declare function fn(x: HTMLDivElement): string;var myElem: HTMLDivElement;var x = fn(myElem); // x: unknown, wat?
✅ 行ってください: より具体的なシグネチャの後により一般的なシグネチャを配置することで、オーバーロードをソートしてください。
ts
/* OK */declare function fn(x: HTMLDivElement): string;declare function fn(x: HTMLElement): number;declare function fn(x: unknown): unknown;var myElem: HTMLDivElement;var x = fn(myElem); // x: string, :)
❔ 理由: TypeScriptは、関数呼び出しを解決する際に、最初に一致するオーバーロードを選択します。前のオーバーロードが後のオーバーロードよりも「一般的」な場合、後のオーバーロードは事実上非表示になり、呼び出すことができなくなります。
オプションパラメーターの使用
❌ しないでください: 末尾のパラメーターのみが異なる複数のオーバーロードを記述しないでください。
ts
/* WRONG */interface Example {diff(one: string): number;diff(one: string, two: string): number;diff(one: string, two: string, three: boolean): number;}
✅ 行ってください: 可能な限りオプションパラメーターを使用してください。
ts
/* OK */interface Example {diff(one: string, two?: string, three?: boolean): number;}
すべてのオーバーロードが同じ戻り値型を持つ場合にのみ、この統合を行う必要があります。
❔ 理由: これには2つの重要な理由があります。
TypeScriptは、ターゲットのシグネチャのいずれかをソースの引数で呼び出すことができるかどうかを確認し、余分な引数は許容されます。たとえば、このコードは、シグネチャがオプションパラメーターを使用して正しく記述されている場合にのみ、バグを公開します。
ts
function fn(x: (a: string, b: number, c: number) => void) {}var x: Example;// When written with overloads, OK -- used first overload// When written with optionals, correctly an errorfn(x.diff);
2つ目の理由は、コンシューマーがTypeScriptの「厳格なnullチェック」機能を使用する場合です。指定されていないパラメーターはJavaScriptでは`undefined`として表示されるため、オプション引数を持つ関数に明示的な`undefined`を渡しても問題ありません。たとえば、このコードは厳格なnullチェック下でも問題ありません。
ts
var x: Example;// When written with overloads, incorrectly an error because of passing 'undefined' to 'string'// When written with optionals, correctly OKx.diff("something", true ? undefined : "hour");
ユニオン型の使用
❌ しないでください: 1つの引数の位置での型だけが異なるオーバーロードを記述しないでください。
ts
/* WRONG */interface Moment {utcOffset(): number;utcOffset(b: number): Moment;utcOffset(b: string): Moment;}
✅ 行ってください: 可能な限りユニオン型を使用してください。
ts
/* OK */interface Moment {utcOffset(): number;utcOffset(b: number | string): Moment;}
シグネチャの戻り値型が異なるため、ここでは`b`をオプションにしませんでした。
❔ 理由: これは、「値を関数に渡している」人にとって重要です。
ts
function fn(x: string): Moment;function fn(x: number): Moment;function fn(x: number | string) {// When written with separate overloads, incorrectly an error// When written with union types, correctly OKreturn moment().utcOffset(x);}