TypeScript 関数の種類

導入

関数の型宣言では、関数の宣言時にパラメーターの型と戻り値の型を指定する必要があります。

関数こんにちは(
  txt: 文字列
):空所 {
  console.log('hello ' + txt);
}

上記の例では、関数 hello() を宣言する際に、パラメータ txt の型 (文字列) と戻り値の型 (void) をその後に記述する必要があります。パラメータリスト内の括弧。 void 型は戻り値がないことを意味します。詳細については以下を参照してください。

パラメータのタイプが指定されていない場合 (たとえば、上記の例では txt のタイプが記述されていない場合)、TypeScript はパラメータのタイプを推測します。情報が不十分な場合は、パラメータのタイプが any であると推測します。 。

戻り値の型は TypeScript が自動的に推測するため、通常は記述する必要はありません。

関数 hello(txt:string) {
  console.log('hello ' + txt);
}

上の例では、return ステートメントがないため、TypeScript は関数 hello() には戻り値がないと推測します。

ただし、戻り値の型は、文書化の目的で、または戻り値が誤って変更されるのを防ぐために記述される場合があります。

変数が関数に割り当てられる場合、変数の型は 2 つの方法で記述できます。

//書き方その1
const hello = 関数 (txt:string) {
  console.log('hello ' + txt);
}

//書き方2
定数こんにちは:
  (txt:文字列) => 無効
= 関数 (テキスト) {
  console.log('hello ' + txt);
};

上の例では、変数 hello が関数に割り当てられており、その型は 2 つの方法で記述することができます。 1 つ目の記述方法は、等号の右側にある関数の型を使用して変数 hello の型を推測することです。2 つ目の記述方法は、アロー関数の形式を使用して変数 の型を指定することです。 hello。矢印の左側にパラメータの型が書かれており、矢印の右側に戻り値の型が書かれています。

書き方2の注意点は2つあります。

まず、関数のパラメータを括弧で囲む必要があります。そうしないと、エラーが報告されます。

次に、型内のパラメータ名 (この場合は txt) が必要です。一部の言語(C言語など)の関数型ではパラメータ名を記述できませんが、TypeScriptでは記述できません。 (string) => void と記述すると、TypeScript は関数に string という名前のパラメータがあり、この string パラメータの型が any であることを認識します。

type MyFunc = (文字列, 数値) => 数値;
// (文字列: 任意、数値: 任意) => 数値

上記の例では、関数の型にパラメーター名が含まれていないため、TypeScript はパラメーターの型がすべて「any」であると認識します。

関数タイプ内のパラメータ名と実際のパラメータ名は一致しない可能性があります。

let f:(x:number) => 数値;
 
f = 関数 (y:数値) {
  y を返します。
};

上記の例では、関数の型のパラメータ名は「x」ですが、実際の関数定義ではパラメータ名は「y」になります。

関数の型定義が非常に長い場合、または複数の関数が同じ型を使用する場合、2 番目の方法を使用するのは非常に面倒になります。したがって、type コマンドは、他の変数への割り当てを容易にするために、関数タイプのエイリアスを定義するためによく使用されます。

type MyFunc = (txt:string) => void;

const hello:MyFunc = 関数 (txt) {
  console.log('hello ' + txt);
};

上記の例では、type コマンドは関数タイプのエイリアス MyFunc を定義しています。これは後で使用するのに非常に便利です。変数をこのタイプとして指定できます。

関数の実際のパラメーターの数は、型で指定されたパラメーターの数よりも少なくても構いませんが、それを超えることはできません。つまり、TypeScript ではパラメーターを省略できます。

myFunc にさせます:
  (a:数値、b:数値) => 数値;

myFunc = (a:number) => a;

myFunc = (
  a:数値、b:数値、c:数値
) => a + b + c // エラー;

上記の例では、変数 myFunc の型は 2 つのパラメーターのみを受け入れることができ、パラメーターが 1 つだけの関数に割り当てられた場合、エラーは報告されません。ただし、パラメータが 3 つある関数に割り当てた場合は、エラーが報告されます。

これは、JavaScript 関数は宣言時に冗長なパラメーターを持つことが多く、実際に使用するときにパラメーターの一部しか渡せないためです。たとえば、配列の forEach() メソッドのパラメータは関数であり、デフォルトでは 3 つのパラメータ (item, Index, array) => void があります。実際には、最初のパラメータ `(item) のみです。 => がよく使われます。したがって、TypeScript では関数が不十分な引数を渡すことを許可します。

x = (a:数値) => 0; とします。
y = (b:数値, s:文字列) => 0; とします。

y = x; // 正しい
x = y // エラー

上の例では、関数 x には 1 つのパラメータしかなく、関数 y には 2 つのパラメータがありますが、その逆はできません。

変数を別の関数型にする必要がある場合は、「typeof」演算子を使用するというちょっとしたコツがあります。

関数 add(
  x:数値、
  y:数値
) {
  x + y を返します。
}

const myAdd:typeof add = function (x, y) {
  x + y を返します。
}

上記の例では、関数 myAdd() の型は関数 add() と同じであるため、typeof add として定義できます。関数名 add 自体は型ではなく値であるため、その型は typeof 演算子を使用して返される必要があります。

これは便利なトリックです。型が必要な場合はどこでも、「typeof」演算子を使用して値から型を取得できます。

関数タイプはオブジェクトの形式で記述することもできます。

追加しましょう:{
  (x:数値、y:数値):数値
};
 
add = 関数 (x, y) {
  x + y を返します。
};

上記の例では、変数「add」の型をオブジェクトとして記述しています。

関数型のオブジェクトは次のように記述します。

{
  (パラメータリスト): 戻り値
}

この書き方では、関数のパラメータと戻り値の間の区切り文字が、通常の書き方の矢印「=>」ではなくコロン「:」になっていることに注意してください。これは、ここにオブジェクトの型と、オブジェクトの属性名と属性が記述されるためです。値の間にはコロンが使用されます。

この書き方はめったに使用されませんが、関数自体に属性がある場合に非常に適しています。

関数 f(x:数値) {
  コンソール.ログ(x);
}

f.version = '1.0';

上記の例では、関数 f() 自体にも属性 version があります。このとき、f は完全にオブジェクトなので、型をオブジェクト形式で記述する必要があります。

foo にしましょう: {
  (x:数値): 無効;
  バージョン: 文字列
} = f;

関数の型は、インターフェイスを使用して宣言することもできます。この書き込みメソッドは、オブジェクトの書き込みメソッドのレプリカです。詳細については、「インターフェイス」の章を参照してください。

インターフェイス myfn {
  (a:数値、b:数値):数値;
}

var add:myfn = (a, b) => a + b;

上記の例では、インターフェイス コマンドはインターフェイス 'myfn' を定義しており、このインターフェイスの型はオブジェクトによって表される関数です。

関数の種類

TypeScript は関数を表す Function 型を提供しており、あらゆる関数はこの型に属します。

関数 doSomething(f:Function) {
  f(1, 2, 3) を返します。
}

上記の例では、パラメータ f の型は Function であり、これは関数であることを意味します。

Function typeの値を直接実行できます。

Function 型の関数は、任意の数のパラメータを受け入れることができ、各パラメータの型も "any" であるため、これを使用することはお勧めできません。 type 関数の詳細な型宣言を与えるとよいでしょう。

アロー関数

アロー関数は通常の関数を簡略化して記述したもので、型の記述も通常の関数と同様です。

const 繰り返し = (
  str:文字列、
  回:数値
):string => str.repeat(回);

上記の例では、アロー関数に変数repeatを代入し、アロー関数の定義内に型宣言を記述しています。このうち、パラメータの型はパラメータ名の後に記述され、戻り値の型はパラメータリストの括弧の後に記述されます。

型はアロー関数の定義に記述されます。これは、アロー関数を使用して関数の型を表すのとは異なります。

関数挨拶(
  fn:(a:string) => void
):空所 {
  fn('世界');
}

上記の例では、関数 greet() のパラメータ fn が関数であり、型はアロー関数で表されます。このとき、「fn」の戻り値の型はパラメータリストの括弧の後ろではなく、矢印の右側に書きます。

別の例を見てみましょう。

type 人 = { 名前: 文字列 };

const people = ['アリス', 'ボブ', 'ジャン'].map(
  (名前):人 => ({名前})
);

上の例では、person は属性 name を持つオブジェクトを表す型エイリアスです。変数 people は、配列の map() メソッドの戻り値です。

map() メソッドのパラメータはアロー関数 (name):person => ({name}) です。アロー関数のパラメータ name の型は から取得できるため省略します。 map() の型 アロー関数の戻り値の型は定義から推測されます。したがって、変数peopleの型はperson[]となります。

矢印の後の ({name}) は、オブジェクトが name という属性を持ち、その属性値が変数 name の値であることを意味します。ここでの括弧は必須です。そうでない場合は、(name):person => {name} の中括弧が関数本体を表します。つまり、関数本体に name というステートメントの行が存在します。 return ステートメントがないため、この関数は値を返しません。

以下の 2 つの書き方は誤りですので注意してください。

// 間違い
(名前:人) => ({名前})

// 間違い
名前:人 => ({名前})

この場合、上記の 2 つの書き方はどちらも間違いです。 1つ目の書き方は、アロー関数のパラメータ「name」の型が「person」で、関数の戻り値の型は書かず、TypeScript自身が推論できるようにする方法です。 2 番目の書き方では、関数パラメータに括弧がありません。

オプションのパラメータ

関数のパラメータを省略できる場合は、パラメータ名の後に疑問符を追加します。

関数 f(x?:数値) {
  // ...
}

f(); // OK
f(10); // OK

上の例では、パラメータ「x」の後に疑問符があり、パラメータが省略できることを示しています。

パラメータ名の疑問符は、パラメータのタイプが実際には raw type|unknown であり、未定義 である可能性があることを示します。たとえば、上の例では x の型は number として宣言されていますが、実際には number|unknown です。

関数 f(x?:数値) {
  x を返します。
}

f(未定義) // 正しい

上の例では、パラメータ x はオプションであり、これは x に値 unknown を割り当てることができると言っているのと同じです。

ただし、その逆は当てはまりません。型が明示的に「未定義」に設定されているパラメータは省略できません。

関数 f(x:数値|未定義) {
  x を返します。
}

f() // エラーを報告する

上の例では、パラメータ x のタイプは number|undefined です。これは、値または unknown のいずれかが渡されることを意味します。このパラメータを省略すると、エラーが報告されます。

関数のオプションのパラメーターは、必須パラメーターに続く、パラメーター リストの最後にのみ配置できます。

myFunc にさせます:
  (a?:数値, b:数値) => 数値; // エラーレポート;

上記の例では、オプションのパラメータが必須パラメータの前にある場合、エラーが報告されます。

フロントパラメータが空の場合は、パラメータタイプが「未定義」である可能性があることを明示的に示すことしかできません。

myFunc にさせます:
  (
    a:数値|未定義、
    b:数字
  ) => 数値;

上記の例では、パラメータ a が空である可能性があるため、型に undefined が含まれていることを明示的に示すことのみが可能で、パラメータを渡すときに明示的に unknown を渡す必要もあります。

オプションのパラメータが関数本体内で使用される場合、パラメータが「未定義」かどうかを判断する必要があります。

myFunc にさせます:
  (a:数値, b?:数値) => 数値;

myFunc = 関数 (x, y) {
  if (y === 未定義) {
    x を返します。
  }
  x + y を返します。
}

上記の例では、関数の 2 番目のパラメーターはオプションのパラメーターであるため、関数本体内でパラメーターが空かどうかを判断する必要があります。

パラメータのデフォルト値

TypeScript関数のデフォルトのパラメータ値の記述方法はJavaScriptと一致しています。

デフォルト値が設定されているパラメータはオプションです。このパラメータが渡されない場合、デフォルト値と等しくなります。

関数createPoint(
  x:数値 = 0、
  y:数値 = 0
):[数値, 数値] {
  [x, y] を返します。
}

createPoint() // [0, 0]

上記の例では、パラメータ xy のデフォルト値は両方とも 0 ですが、createPoint() を呼び出す場合、これら 2 つのパラメータは省略できます。 xy の型宣言はデフォルト値から推測できるため、実際にはここでは省略できます。

関数createPoint(
  x = 0、y = 0
) {
  [x, y] を返します。
}

オプションのパラメータとデフォルト値を同時に使用することはできません。

// エラーを報告する
関数 f(x?: 数値 = 0) {
  // ...
}

上記の例では、「x」はオプションのパラメータであり、デフォルト値が設定されていますが、エラーが報告されます。

デフォルト値を持つパラメータの場合、「unknown」が渡されると、デフォルト値もトリガーされます。

関数 f(x = 456) {
  x を返します。
}

f(未定義) // 456

デフォルト値を持つパラメータがパラメータ リストの最後にない場合、呼び出し時にそのパラメータを省略することはできません。デフォルト値をトリガーしたい場合は、明示的に「unknown」を渡す必要があります。

関数 add(
  x:数値 = 0、
  y:数値
) {
  x + y を返します。
}

add(1) // エラーを報告する
add(未定義, 1) // 正しい

パラメータの構造化

関数の引数に変数の構造化がある場合、型は次のように記述されます。

関数 f(
  [x, y]: [数値, 数値]
) {
  // ...
}

関数の合計(
  { a、b、c }: {
     a:数字。
     b:数字;
     c: 数字
  }
) {
  console.log(a + b + c);
}

パラメーターの構造化を型エイリアス (type コマンド) と組み合わせて使用​​すると、コードがより単純に見えます。

タイプ ABC = { a: 数値; c: 数値 };

関数 sum({ a, b, c }:ABC) {
  console.log(a + b + c);
}

残りのパラメータ

残りのパラメーターは、関数の残りのすべてのパラメーターを表し、配列 (残りのパラメーターは同じ型) またはタプル (残りのパラメーターは異なる型) にすることができます。

// 残りのパラメータは配列です
function joinNumbers(...nums:number[]) {
  // ...
}

// 残りのパラメータはタプルです
関数 f(...args:[ブール値, 数値]) {
  // ...
}

タプルは残りの各パラメータの型を宣言する必要があることに注意してください。タプル内のパラメータがオプションの場合は、オプションのパラメータを使用します。

関数 f(
  ...args: [ブール値、文字列?]
) {}

以下はrestパラメータの例です。

関数 multiply(n:数値, ...m:数値[]) {
  return m.map((x) => n * x);
}

上記の例では、パラメータ m は残りの型であり、その型は配列です。

残りのパラメータはネストすることもできます。

function f(...args:[boolean, ...string[]]) {
  // ...
}

REST パラメーターは、変数の構造化と組み合わせて使用​​できます。

関数の繰り返し(
  ...[文字列、回数]: [文字列、数値]
):弦 {
  str.repeat(回)を返します;
}

// と同等
関数の繰り返し(
  str: 文字列、
  回: 数値
):弦 {
  str.repeat(回)を返します;
}

readonly 読み取り専用パラメータ

関数内でパラメーターを変更できない場合は、関数を定義するときにパラメーター タイプの前に「readonly」キーワードを追加して、これが読み取り専用パラメーターであることを示すことができます。

関数 arraySum(
  arr:読み取り専用番号[]
) {
  // ...
  arr[0] = 0; // エラーレポート
}

上の例では、パラメータ arr のタイプは readonly number[] であり、これは読み取り専用パラメータであることを意味します。この配列が関数本体内で変更されると、エラーが報告されます。

readonly キーワードは現在、配列およびタプル型のパラメータの前でのみ使用できることに注意してください。他の型のパラメータの前で使用すると、エラーが報告されます。

ボイド型

void 型は、関数に戻り値がないことを示します。

関数 f():void {
  console.log('こんにちは');
}

上記の例では、関数 f には戻り値がないため、型は void として記述する必要があります。

他の値が返された場合は、エラーが報告されます。

関数 f():void {
  return 123; // エラーレポート
}

上の例では、関数 f() の戻り値の型は void となっていますが、実際には数値が返されるため、コンパイル時にエラーが報告されます。

void 型では、「未定義」または「null」を返すことができます。

関数 f():void {
  // 正しい値を返します。
}

関数 f():void {
  null を返す // 正しい。
}

strictNullChecks コンパイル オプションがオンになっている場合、void 型は unknown を返すことのみが許可されます。 「null」が返された場合、エラーが報告されます。これは、JavaScript が関数が値を返さない場合、それは「未定義」を返すのと同じであると規定しているためです。

// コンパイル オプション strictNullChecks をオンにします

関数 f():void {
  // 正しい値を返します。
}

関数 f():void {
  null を返す // エラーレポート
}

変数、オブジェクト メソッド、または関数パラメーターが void 型の戻り値を持つ関数である場合、それを戻り値を持つ関数に代入できないわけではないことに注意することが重要です。逆に、変数、オブジェクト メソッド、および関数パラメーターは、任意の値を返す関数を受け入れることができ、エラーは報告されません。

タイプ voidFunc = () => void;

const f:voidFunc = () => {
  123 を返します。
};

上記の例では、変数 f の型は voidFunc であり、戻り値のない関数です。しかし実際には、f の値は戻り値 (123 を返す) を持つ関数にすることができ、コンパイル中にエラーは報告されません。

これは、TypeScript では、ここでの void 型は単に関数の戻り値に使用価値がない、または関数の戻り値を使用すべきではないことを意味すると考えているためです。ここでの戻り値が使用されない限り、エラーは報告されません。

この設計には実用的な意味があります。たとえば、配列メソッド Array.prototype.forEach(fn) のパラメータ fn は関数であり、この関数には戻り値がありません。つまり、戻り値の型は void です。

ただし、実際のアプリケーションでは、渡された関数に戻り値がある場合が多くありますが、その戻り値は重要ではないか、何の効果もありません。

const src = [1, 2, 3];
const ret = [];

src.forEach(el => ret.push(el));

上記の例では、push() には新しい要素を挿入した後の配列の長さを示す戻り値があります。ただし、forEach() メソッドの場合、この戻り値は効果がなく、まったく使用されないため、TypeScript はエラーを報告しません。

この関数の戻り値を後で使用すると規約に違反し、エラーが報告されます。

タイプ voidFunc = () => void;
 
const f:voidFunc = () => {
  123 を返します。
};

f() * 2 // エラーを報告

上記の例では、最後の行でエラーが報告されています。これは、型宣言によれば、f() には戻り値がありませんが、その戻り値が使用されるため、エラーが報告されます。

この状況は、変数、オブジェクト メソッド、および関数パラメータに限定されていることに注意してください。関数リテラルが戻り値が void 型であると宣言している場合でも、戻り値を持つことはできません。

関数 f():void {
  true を返す // エラーレポート
}
 
const f3 = function ():void {
  true を返す // エラーレポート
};

上記の例では、関数リテラルは戻り値の型 void を宣言しています。この場合、戻り値 (unknownnull を除く) がある限り、エラーが報告されます。

関数の実行結果がエラーをスローする場合、戻り値を void として記述することもできます。

関数 throwErr():void {
  throw new Error('何か間違っている');
}

上記の例では、関数 throwErr() はエラーをスローし、戻り値の型は void として記述されます。

関数を除いて、他の変数を void 型として宣言することはあまり意味がありません。その場合、変数は unknown または null にのみ代入できるからです (strictNullChecks がオンになっていないと仮定します)。

foo:void = 未定義にしてみましょう。

// strictNullChecks をオンにしない場合
bar:void = null にします。

決して入力しないでください

「never」タイプは、絶対に発生しない値を表します。関数の戻り値に使用され、ある関数が絶対に値を返さない、つまり関数が正常に終了しないことを意味します。

主に以下の2つの状況が考えられます。

(1) エラーをスローする関数。

関数失敗(msg:string):never {
  新しいエラー(msg)をスローします。
}

上記の例では、関数 fail() はエラーをスローし、正常に終了しないため、戻り値の型は never になります。

スローエラーのみが Never タイプであることに注意してください。 return ステートメントを明示的に使用して Error オブジェクトを返す場合、戻り値の型は none ではありません。

関数fail():エラー{
  return new Error("何かが失敗しました");
}

上記の例では、関数 fail() は Error オブジェクトを返すため、戻り値の型は Error になります。

また、スローされるエラーはnever型またはvoid型に属するため、戻り値の型からはどちらのエラーがスローされたかを知ることはできません。

(2) 無限に実行できる関数。

const sing = function():never {
  while (true) {
    console.log('歌う');
  }
};

上記の例では、関数 sing() は永久に実行され戻りません。そのため、戻り値の型は never になります。

「never」タイプは「void」タイプとは異なることに注意してください。前者は、関数の実行が完了しておらず、戻り値がないことを意味し、後者は、関数の実行が正常に終了したが、値を返さない、または「未定義」を返すことを意味します。

// 正しい
関数 sing():void {
  console.log('歌う');
}

// エラーを報告する
関数 sing():never {
  console.log('歌う');
}

上記の例では、関数 sing() には return 文がありませんが、実際には return undefined の行が省略されており、実際の戻り値は unknown になります。したがって、戻り値の型は「never」ではなく「void」と書く必要があります。「never」と書くとエラーになります。

関数が例外をスローしたり、無限ループに陥った場合、関数は正常に値を返すことができないため、関数の戻り値の型は never になります。戻り値の型が「never」の関数がプログラム内で呼び出された場合、プログラムは関数の呼び出し位置で終了し、後続のコードの実行は続行されないことを意味します。

関数neverReturns():never {
  新しい Error() をスローします。
}

関数 f(
  x:文字列|未定義
) {
  if (x === 未定義) {
    決して戻りません();
  }

  x; // 文字列として推論される
}

上記の例では、関数 f() のパラメータ x の型は string|unknown です。ただし、x 型が undefine の場合は、neverReturns() が呼び出されます。この関数は返さないため、TypeScript は判定ステートメントの後の x の型が string である必要があると推測できます。

関数が特定の条件下では正常な戻り値を持ち、他の条件下ではエラーをスローする場合、その戻り値の型は「never」を省略できます。

関数時々Throw():number {
  if (Math.random() > 0.5) {
    100を返します。
  }

  throw new Error('何か問題が発生しました');
}

const result = 時々Throw();

上記の例では、関数 sometimeThrow() の戻り値は実際には number|never ですが、通常は最後の行の変数 result の型を含めて number として記述されます。 「数値」としても推論されます。

その理由は、前の章で述べたように、TypeScript の基になる型は「never」のみであり、他のすべての型には「never」が含まれるためです。集合論の観点から見ると、「number|never」は「number」と同等です。これは、関数の戻り値の型に関係なく、エラーのスローが含まれる可能性があることも思い出させます。

ローカルタイプ

他の型は関数内で宣言できます。この型は関数内でのみ有効であり、ローカル型と呼ばれます。

関数 hello(txt:string) {
  タイプメッセージ = 文字列;
  let newTxt:message = 'hello ' + txt;
  newTxt を返します。
}

const newTxt:message = hello('world'); // エラーレポート

上記の例では、型 message は関数 hello() 内で定義されており、関数内でのみ使用できます。関数外で使用するとエラーが報告されます。

高階関数

関数の戻り値が関数のままである場合、前の関数は高階関数と呼ばれます。

以下はアロー関数の例です。

(someValue: 数値) => (乗数: 数値) => someValue * 乗数;

関数のオーバーロード

一部の関数はさまざまなタイプまたは数のパラメーターを受け入れることができ、パラメーターに応じて関数の動作が異なります。さまざまなパラメーターの型に基づいてさまざまなロジックを実行するこの動作は、関数のオーバーロードと呼ばれます。

reverse('abc') // 'cba'
reverse([1, 2, 3]) // [3, 2, 1]

上記の例では、関数 reverse() によってパラメータを逆に出力できます。パラメータには文字列または配列を使用できます。

これは、関数内に文字列と配列を処理するための 2 つのロジック セットがあり、対応するロジックが異なるパラメーターの型に応じてそれぞれ実行されることを意味します。これを「関数のオーバーロード」と呼びます。

「関数のオーバーロード」の型宣言に対する TypeScript のアプローチは、それぞれの場合に 1 つずつ型を定義することです。

関数リバース(str:string):string;
関数 reverse(arr:any[]):any[];

上記の例では、関数 reverse() の 2 つのパラメーターの状況に対して型宣言が与えられています。しかし、これで終わりではなく、後で完全な型宣言を関数 reverse() に与える必要があります。

関数リバース(str:string):string;
関数 reverse(arr:any[]):any[];
関数リバース(
  文字列または配列:文字列|任意[]
):文字列|任意[] {
  if (文字列または配列の型 === '文字列')
    stringOrArray.split('').reverse().join(''); を返します。
  それ以外
    stringOrArray.slice().reverse() を返します。
}

上記の例では、型宣言の最初の 2 行に、さまざまなオーバーロードのケースがリストされています。 3 行目は関数自体の型宣言であり、前のオーバーロード宣言と互換性がある必要があります。

一部のプログラミング言語では、さまざまな関数の実装に対応して、さまざまな関数パラメータを使用できます。ただし、JavaScript 関数の実装は 1 つだけであり、この実装ではさまざまなパラメーターを処理する必要があります。そのため、関数本体ではパラメータの種類と数を判断し、その判断結果に基づいて別の演算を行う必要があります。

関数 add(
  x:数値、
  y:数値
):番号;
関数 add(
  x:任意[]、
  y:任意[]
):どれでも[];
関数 add(
  x:数値|任意[]、
  y:数値|任意[]
):数値|任意[] {
  if (x の種類 === '数値' && y の種類 === '数値') {
    x + y を返します。
  else if (Array.isArray(x) && Array.isArray(y)) {
    [...x, ...y] を返します。
  }

  throw new Error('間違ったパラメータ');
}

上記の例では、関数 add() は内部で if コード ブロックを使用して、パラメーターの 2 つの状況をそれぞれ処理します。

オーバーロードされた型の説明と関数の特定の実装の間に他のコードを含めることはできないことに注意してください。そうでない場合は、エラーが報告されます。

さらに、関数の特定の実装には完全な型宣言があります。ただし、関数によって実際に呼び出される型は、前の型宣言に従います。たとえば、上記の例の関数実装では、パラメータの型と戻り値の型が両方とも number|any[] ですが、パラメータの型が次の場合に戻り値の型が any[] になるわけではありません。 「番号」。

関数オーバーロードの各型宣言の間、および型宣言と関数によって実装される型の間に競合があってはなりません。

// エラーを報告する
関数 fn(x:boolean):void;
関数 fn(x:string):void;
関数 fn(x:数値|文字列) {
  コンソール.ログ(x);
}

上記の例では、関数のオーバーロードの型宣言が関数の実装と競合し、エラーが発生します。

TypeScript は、特定の型宣言が見つかるとそれ以降はチェックされなくなるため、オーバーロードされた宣言の順序は重要です。そのため、他の型宣言を上書きしないように、最も幅の広い型の宣言を最後に配置する必要があります。

関数 f(x:any):数値;
関数 f(x:string): 0|1;
関数 f(x:any):any {
  // ...
}

const a:0|1 = f('hi'); // エラーレポート

上記のステートメントでは、型宣言 x:any の最初の行のスコープが最も広いため、関数 f() の呼び出しはステートメントのこの行と一致しますが、型宣言の 2 行目と一致することはできません。呼び出しの最後の行では、等号の両側の型が一致しないため、エラーが報告されます。左側の型は 0|1 で、右側の型は number です。この関数のオーバーロードの正しい順序は、型宣言の 2 行目を 1 行目に置くことです。

オブジェクト メソッドでもオーバーロードを使用できます。

クラス StringBuilder {
  #データ = '';

  add(num:number): これ;
  add(bool:boolean): これ;
  add(str:string): これ;
  add(値:任意): this {
    this.#data += 文字列(値);
    これを返します。
  }

  toString() {
    これを返します。#data;
  }
}

上記の例では、メソッド add() も関数のオーバーロードを使用しています。

関数のオーバーロードを使用して、関数のパラメーターと戻り値の対応を正確に記述することもできます。

関数createElement(
  タグ:「あ」
):HTMLAnchorElement;
関数createElement(
  タグ:「キャンバス」
):HTMLCanvasElement;
関数createElement(
  タグ:「テーブル」
):HTMLTableElement;
関数createElement(
  タグ:文字列
):HTML要素 {
  // ...
}

上記の例では、関数のオーバーロードは、パラメーター tag の 3 つの値と、対応するさまざまな関数の戻り値を正確に記述しています。

この例の関数のオーバーロードはオブジェクトによって表すこともできます。

typeCreateElement = {
  (タグ:'a'): HTMLAnchorElement;
  (タグ:'キャンバス'): HTMLCanvasElement;
  (タグ:'テーブル'): HTMLTableElement;
  (タグ:文字列): HTML要素;
}

オーバーロードは比較的複雑な型宣言方法であるため、複雑さを軽減するために、一般的に、複数のパラメーターがある場合、またはパラメーターと戻り値の間にある場合を除き、可能であれば関数のオーバーロードの代わりに共用体型を使用する必要があります。それらの間の対応関係。

//書き方その1
関数 len(s:string):number;
関数 len(arr:any[]):数値;
関数 len(x:any):数値 {
  x.length を返します。
}

//書き方2
関数 len(x:any[]|string):number {
  x.length を返します。
}

上記の例では、2 番目の書き方では共用体型を使用しています。これは、最初の書き方の関数のオーバーロードよりもはるかに単純です。

コンストラクター

JavaScript 言語はコンストラクターを使用してオブジェクトのインスタンスを生成します。

コンストラクタの最大の特徴は、new コマンドを使用して呼び出す必要があることです。

const d = 新しい日付();

上の例では、Date() はコンストラクターであり、new コマンドを使用して呼び出され、Date オブジェクトのインスタンスを返します。

コンストラクターの型を記述する方法は、パラメーター リストの前に new コマンドを追加することです。

クラス動物{
  numLegs:数値 = 4;
}

タイプ AnimalConstructor = new () => Animal;

function create(c:AnimalConstructor):Animal {
  新しい c() を返します。
}

const a = create(動物);

上記の例では、型 AnimalConstructor はコンストラクターであり、関数 create() はコンストラクターに渡す必要があります。 JavaScript では、クラスは本質的にコンストラクターであるため、クラス Animalcreate() に渡すことができます。

コンストラクターをオブジェクト形式で記述する別の方法もあります。

タイプ F = {
  新しい(s:文字列):オブジェクト;
};

上記の例では、タイプ F はコンストラクターです。タイプは実行可能オブジェクトの形式で記述され、パラメータ リストの前に「new」コマンドが追加されます。

一部の関数はコンストラクターであり、「Date()」などの通常の関数として使用できます。このとき、型宣言は以下のように書くことができます。

タイプ F = {
  新しい(s:文字列):オブジェクト;
  (n?:数値): 数値;
}

上記の例では、F は通常の関数またはコンストラクターとして実行できます。


作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0