べき・べからず集

一般的な型

Number、String、Boolean、Object

NumberStringBooleanObjectの型を使用しないでください。 これらの型は、JavaScriptコードでほとんど使用されない非プリミティブなボックス化されたオブジェクトを示します。

/* 誤 */
function reverse(s: String): String;

numberstringbooleanの型を使用してください。

/* 正 */
function reverse(s: string): string;

Object型を使用したい場合は、代わりにanyを使用することを検討してください。 現時点では、TypeScriptには「プリミティブではない」オブジェクトを指定する方法はありません。

ジェネリクス

型のパラメータを使用しないジェネリック型は、決して使用しないでください。 詳細については、TypeScriptのFAQページを参照してください。

コールバックの型

コールバックが返す型

値が無視されるコールバックには、戻り値の型にanyを使用しないでください。

/* 誤 */
function fn(x: () => any) {
    x();
}

値が無視されるコールバックには、戻り値の型にはvoidを使用してください。

/* 正 */
function fn(x: () => void) {
    x();
}

理由:
検査されない状態でxの戻り値が誤って使用されることを防いでくれるため、 voidを使用することでより安全になります。

function fn(x: () => void) {
    var k = x();     // 何か別のことを意図していたのかも!?
    k.doSomething(); // エラー、ただし戻り値が'any'型の場合はOKになってしまいます
}

コールバック内の任意引数

あなたが本当にそれに意味があると考え必要としない限りは、コールバックに任意の引数を使用しないでください。

/* 誤 */
interface Fetcher {
    getObject(done: (data: any, elapsedTime?: number) => void): void;
}

これは具体的には、doneコールバックは、1つまたは2つの引数で呼び出すことができることを意味します。

プログラムの著者はおそらく、コールバックがelapsedTime引数を気にしなくても良いということを意図しているのでしょうが、 このために引数を任意にする必要はありません。 少ない引数でも受け入れるコールバックを提供することは常に合法なのです。

コールバックの引数を必須として指定してください。

/* OK */
interface Fetcher {
    getObject(done: (data: any, elapsedTime: number) => void): void;
}

オーバーロードとコールバック

コールバックのarity(訳注: 引数の数?)だけが異なるオーバーロードを別々に書かないでください。

/* 誤 */
declare function beforeAll(action: () => void, timeout?: number): void;
declare function beforeAll(action: (done: DoneFn) => void, timeout?: number): void;

最大数のarityを使用して、一つのオーバーロードを書いてください。

/* 正 */
declare function beforeAll(action: (done: DoneFn) => void, timeout?: number): void;

理由:
コールバックが引数を無視(軽視?)することは常に合法なのです。 したがって、オーバーロードを短くする必要はありません。

1つ目に短いコールバックを提供してしまうと、不正な型が指定された関数が、 その1つ目のオーバーロードとマッチしてしまうために渡されることを許してしまいます。

関数のオーバーロード

順番

より具体的なオーバーロードより前に、より曖昧なオーバーロードを入れないでください。

/* 誤 */
declare function fn(x: any): any;
declare function fn(x: HTMLElement): number;
declare function fn(x: HTMLDivElement): string;

var myElem: HTMLDivElement;
var x = fn(myElem); // x: any かな?

オーバーロードは、より曖昧なシグネチャは、より具体的なシグネチャの後に並べてください。

/* 正 */
declare function fn(x: HTMLDivElement): string;
declare function fn(x: HTMLElement): number;
declare function fn(x: any): any;

var myElem: HTMLDivElement;
var x = fn(myElem); // x: string, :)

理由:
TypeScriptは、関数呼び出しを解決する際に最初にマッチするオーバーロードを選択します。 前のオーバーロードが後のオーバーロードよりも「より曖昧」である場合、後のオーバーロードは事実上隠されることになり、呼び出すことができなくなります。

任意の引数を使用してください

末端の引数だけが異なる複数のオーバーロードを書かないでください。

/* 誤 */
interface Example {
    diff(one: string): number;
    diff(one: string, two: string): number;
    diff(one: string, two: string, three: boolean): number;
}

可能な限り、任意の引数を使用してください。

/* 正 */
interface Example {
    diff(one: string, two?: string, three?: boolean): number;
}

このように取りまとめるには、すべてのオーバーロードの戻り値の型が同じである必要があることに注意してください。

理由: これには2つの重要な理由があります。

TypeScriptは対象のシグネチャがその引数を使って実行できるのかを確認することで、シグネチャの互換性を解決します。 その際に異質な引数であっても許可されてしまうわけです。

例えば、下記のコードでは任意の引数を使用した正しい署名の場合にのみ、バグが発覚します。

function fn(x: (a: string, b: number, c: number) => void) { }
var x: Example;
// オーバーロードで書かれた場合、1つ目のオーバーロードが使用されてOKと判定される。
// 任意で書かれた場合、正しくエラーが発覚する。
fn(x.diff);

第2の理由は、使用者がTypeScriptの「厳密なnullチェック」機能を使用する場合があることです。

JavaScriptでは指定されていない引数はundefinedとみなされるため、 任意の引数を持つ関数に、明示的にundefinedを渡すことは問題ありません。 例えば、このコードは厳密なnullチェック下で問題ないはずです。

var x: Example;
// オーバーロードで書かれた場合、undefinedが'string'に渡されていると判定あれ、誤ったエラーが発生する。
// 任意の引数で書かれた場合、OKと正しく判定されます。
x.diff("something", true ? undefined : "hour");

共用体型(union types)を使用してください

1つの引数の場所だけの型が異なる場合、オーバーロードにしないでください。

/* 誤 */
interface Moment {
    utcOffset(): number;
    utcOffset(b: number): Moment;
    utcOffset(b: string): Moment;
}

可能な限り、共用体型(union types)を使用してください。

/* 正 */
interface Moment {
    utcOffset(): number;
    utcOffset(b: number|string): Moment;
}

bを任意の引数にしていないことに注意してください。 これは、シグネチャの戻り値の型が異なるためです。

理由:
これは、あなたの関数で値を"通過"させる人にとって重要になります。

function fn(x: string): void;
function fn(x: number): void;
function fn(x: number|string) {
    // オーバーロードが別々の場合、間違ったエラーが発生します。
    // 共用体型(union type)の場合は、OKと正しく判定されます。
    return moment().utcOffset(x);
}

 Back to top

© https://github.com/Microsoft/TypeScript-Handbook

このページは、ページトップのリンク先のTypeScript-Handbook内のページを翻訳した内容を基に構成されています。 下記の項目を確認し、必要に応じて公式のドキュメントをご確認ください。 もし、誤訳などの間違いを見つけましたら、 @tomofまで教えていただければ幸いです。

  • ドキュメントの情報が古い可能性があります。
  • "訳注:"などの断わりを入れた上で、日本人向けの情報やより分かり易くするための追記を行っている事があります。