いくつかの JavaScript ライブラリが reverse
を定義していると仮定しましょう 文字列と配列の両方で機能する関数。いずれの場合も、元の値を変更することなく、入力の逆バージョンを返します:
function reverse(stringOrArray) {
return typeof stringOrArray === "string"
? stringOrArray.split("").reverse().join("")
: stringOrArray.slice().reverse();
}
これは説明目的でのみ使用される単純な実装であることに注意してください。適切な実装では、2 つ以上のコード単位を使用して表される Unicode コード ポイントを処理する必要があります。さらに入力の検証も行います。さらに良いアイデアは、関数を 2 つの別個のものに分割することです。
とはいえ、reverse
はどのように入力すればよいでしょうか TypeScript の関数?
#バージョン #1:任意のタイプ
最も簡単な方法は、パラメーターと戻り値の両方に any
で注釈を付けることです。 JavaScript の任意の値が有効な型:
function reverse(stringOrArray: any): any {
// ...
}
もちろん、このアプローチでは、TypeScript コンパイラはあまり役に立ちません。パラメーターの型に制限を課していないため、コンパイラーは実行時エラーがスローされるパラメーターを問題なく受け入れます。
reverse(true);
reverse({});
reverse(Math.random);
このような間違いを避けるために、もっと具体的にする必要があります。
#バージョン #2:ユニオン型
より洗練された型への次のステップとして、ユニオン型を使用して stringOrArray
パラメータは、任意の型の文字列または要素の配列のいずれかでなければなりません。結果の共用体タイプは string | any[]
です 、これをパラメーターと戻り値の両方の型として使用します:
function reverse(stringOrArray: string | any[]): string | any[] {
// ...
}
これらの型注釈を配置すると、前の例の不適切な呼び出しは型エラーになりますが、正しい呼び出しは許可されます:
reverse(true); // Error!
reverse({}); // Error!
reverse(Math.random); // Error!
const elpmaxe: string | any[] = reverse("example");
const numbers: string | any[] = reverse([1, 2, 3, 4, 5]);
残念ながら、いくつかの型情報が失われました。 numbers
の型 定数は、型 number[]
の引数を渡したことが反映されていません reverse
に 関数。共用体型の 2 番目の構成型が number[]
であると、より便利です。 、 any[]
ではありません .
#バージョン #3:ユニオン型 + ジェネリック
reverse
を入力する少し良い方法 関数は、ジェネリック型を使用することです。配列要素を any
と入力する代わりに 、一般的に T
と入力できます .そうすれば、stringOrArray
パラメータは string
型のいずれかです またはタイプ T[]
.同じことが戻り値にも当てはまります:
function reverse<T>(stringOrArray: string | T[]): string | T[] {
// ...
}
これで、型情報が保持されます:
const elpmaxe: string | string[] = reverse("example");
const numbers: string | number[] = reverse([1, 2, 3, 4, 5]);
率直に言って、関数型はまだ最適ではありません。戻り値の共用体型のため、map
などの配列プロトタイプ メソッドにアクセスできません。 、配列を渡すと配列が返されることはわかっていますが。一方、型システムにはそのような知識がありません。可能なパラメーターと戻り型の組み合わせをまだ正確にモデル化していないためです。
その署名によると、reverse
関数は文字列または配列を受け取り、文字列または配列を返します。別の言い方をすれば、この関数には、次の 4 つのパラメーターと戻り値の型の組み合わせがあります。
(stringOrArray: string) => string
(stringOrArray: string) => T[]
(stringOrArray: T[]) => string
(stringOrArray: T[]) => T[]
しかし、それは reverse
の方法ではありません 関数が動作します。関数の実装を考えると、次の 2 つの組み合わせのみが実行時に発生します:
(stringOrArray: string) => string
(stringOrArray: T[]) => T[]
その知識を型システムに反映する方法を見てみましょう。
#バージョン #4:関数のオーバーロード
他のプログラミング言語では、reverse
をオーバーロードできます。 関数は、同じ名前で異なるタイプの 2 つの関数を記述します:
function reverse(string: string): string {
return string.split("").reverse().join("");
}
function reverse<T>(array: T[]): T[] {
return array.slice().reverse();
}
ただし、これは有効な TypeScript ではありません。同じスコープに同じ名前の 2 つの関数を含めることはできないためです。考えてみてください:上記のコードはどのように JavaScript にトランスパイルされますか? reverse
が 2 つになってしまいます 名前で区別できない関数。
代わりに、TypeScript ではオーバーロード リストを指定して、同じ関数に複数の型を提供できます。そうすれば、型システムに対して、関数が何を受け入れ、何を返すかを正確に記述できます。
function reverse(string: string): string;
function reverse<T>(array: T[]): T[];
function reverse<T>(stringOrArray: string | T[]): string | T[] {
return typeof stringOrArray === "string"
? stringOrArray.split("").reverse().join("")
: stringOrArray.slice().reverse();
}
上記の例の最初の 2 行には、reverse
の有効なオーバーロードがリストされています。 関数。必要に応じて、関数の「外部」シグネチャを表します。 3 行目では、指定されたすべてのオーバーロードと互換性がなければならない汎用の「内部」シグネチャを指定します。これらのオーバーロードが IDE (この場合は Visual Studio) でどのように表示されるかを次に示します。
最初の 2 つのオーバーロードのみがオートコンプリート リストに表示されることに注意してください。共用体型を使用して型付けされた実装自体は表示されません。また、型に応じて、より適切なパラメーター名を指定できるようになったことにも注目してください。以上です!関数のオーバーロードを使用して、reverse
を正確に入力することができました 関数。