Yuki's Tech Blog

仕事で得た知見や勉強した技術を書きます。

【Ruby】【TS】関数のデフォルト引数とオプショナルな引数についてざっくりまとめてみた

目次

背景

仕事で「関数のデフォルト引数ってどうやって指定するんだっけ」と悩んだので、忘れないようにするために記事にしようと思います。

関数のデフォルト引数について

RubyでもTSでもデフォルト引数の書き方は同じです。 デフォルト引数を設定しないで実引数なしで関数を呼び出した場合、Rubyではエラーが起こりますが、TSではundefinedが渡されます。

Ruby

Rubyでは、引数にデフォルト値を設定できます。仮引数の右側に =と デフォルト値を書きます。

def getMessage(message = "Hello world")
  puts message
end

getMessage() # => Hello world

Rubyで引数にデフォルト値を指定したい場合、キーワード引数を使ってデフォルト値を設定することが推奨されています。キーワード引数を使ってデフォルト値を設定することで、引数の順番に左右されることがなくなり、必要な引数だけを指定すれば良くなります。また、キーワード引数の方が実引数の意味が分かりやすく、実引数がどのように使われているかを知るために、メソッド定義を見に行く必要がありません。

キーワード引数を使うためには、仮引数の後ろに:をつけます。そうすると、この仮引数がキーワード引数になります。メソッドを呼び出すときは、引数を名前(キーワード)付きで指定します。キーワード引数にデフォルト値を設定したい場合は、キーワード引数の後ろにデフォルト値を書きます。=は書きません。キーワード引数を使わない場合は、デフォルト値を設定するのに =を書きます。

def getMessage(message1:, message2: "World")
  puts message1 + " " + message2
end

getMessage(message1: "Hello") # => Hello world
getMessage(message1: "Hello", message2: "Japan") # => Hello Japan

TS

TSにおけるデフォルト引数とは、 仮引数に渡ってくる値がundefinedのとき、代わりの値を代入できるような引数のことです。 JSのデフォルト引数は、仮引数の右側に = とデフォルト値を書きます。これはRubyも同じです。

const getMessage = (message = "Hello world") => {
  console.log(message);
};

getMessage(); // => Hello world

TSでは、引数の型の右側に=とデフォルト値を書きます。

const getMessage = (message: string = "Hello world") => {
  console.log(message);
};

getMessage(); // => Hello world

TSではデフォルト引数がある場合、引数の型推論ができます。そのため、引数の型は省略することができます。 基本的には省略します。

const getMessage = (message = "Hello world") => {
  console.log(message);
};

getMessage(); // => Hello world

実引数を省略して関数を呼び出すと、仮引数にはundefinedが渡ります。 引数を省略された場合に、undefinedではなく、あらかじめ設定したデフォルト値を代入したいときにデフォルト引数を利用します。

const getMessage = (message: string) => {
  console.log(message);
};

getMessage(); // => undefined
getMessage("x"); // => "x"

ちなみに、当たり前ですが、デフォルト値が設定されている状態で仮引数に値が渡ってきた場合、その値が使われます。

関数のオプショナルな引数について

Ruby

Rubyではデフォルト値を設定した場合、その引数が省略可能なオプショナルな引数になります。 つまり、Rubyでは引数をデフォルト引数にした場合、その引数はオプショナルな引数にもなります。 TSでも同様です。

def getMessage(message2 = "World", message3: "Hello")
  puts message2 + " " ++ message3
end

getMessage() # => World Hello

しかし、TSの場合、デフォルト値を設定せずに、引数そのものをオプショナルな引数にすることができます。 TSはRubyと違ってデフォルト値を設定しないとオプショナルな引数にできないという縛りはないです。 JSでは実引数を省略すると仮引数にundefinedが渡されていたので、TSのオプショナルな引数でも同じです。

TS

TSでオプショナルな引数を設定することで、関数呼び出し時にその引数を省略することができます。引数をオプショナルな引数にしたい場合、 仮引数名の後ろに?をつけます。関数の仮引数なので、型は必ず書きます。

const getMessage = (message?: string) => {
  console.log(message);
}

getMessage(); // => undefined

オプショナルな引数の型は string | undefinedです。そのため、オプショナルな引数にすることで、JSで引数を指定しないで関数を呼び出した場合に引数にundefinedが代入されるという挙動を満たすことができます。

普通の仮引数にstring | undefined型を指定しても関数呼び出し時に実引数を省略できないので気をつけましょう。オプショナルな引数と同じ挙動を実現するためには、undefinedを渡す必要があります。しかし、省略ができないので、実引数を省略したい場合は、オプショナルな引数を使いましょう。

const getMessage = (message: string | undefined) => {
  console.log(message);
}

// Expected 1 arguments, but got 0.(2554)
getMessage(); 

また、オプショナルな引数は一番最後に書きます。オプショナルな引数の後ろに普通の引数を書くと、コンパイルエラーになります。

// A required parameter cannot follow an optional parameter.

実引数の省略を許容しつつ、仮引数にデフォルト値を代入したい場合、普通に仮引数にデフォルト値を指定して、仮引数をデフォルト引数として定義します。以下の場合、messageがデフォルト引数になります。 仮引数にはundefinedが渡ってきますが、その場合は自動的にHello worldが仮引数に代入されるので、messageがstring型を満たすことになります。

const getMessage = (message = "Hello world") => {
  console.log(message);
};

getMessage(); // => Hello world

キーワード引数について

Ruby

キーワード引数を使うためには、仮引数の後ろに:をつけます。そうすると、この仮引数がキーワード引数になります。メソッドを呼び出すときは、引数を名前(キーワード)付きで指定します。キーワード引数にデフォルト値を設定したい場合は、キーワード引数の後ろにデフォルト値を書きます。キーワード引数を使うことで、引数の順番を気にする必要がなくなったり、実引数がどんな引数なのかが明確になってメソッド定義を見に行く必要がなくなります。ただ、引数の省略はできないので、その場合、キーワード引数にデフォルト値を設定します。

def getMessage(message2: "World", message3:)
  puts message2 + " " + message3
end

getMessage(message3: "world") # => World world

TS

TSでキーワード引数と同じことをする場合、仮引数にオブジェクトを渡して分割代入で受け取れるようにします。その際、分割代入でもデフォルト値を設定できます。以下の例では、message2のデフォルト値として、Worldを指定しています。型を書く際にmessage2をstringとして定義すると、message2を省略することができなくてコンパイルエラーになるので、?を書きます。そうすることで、message2の型がstring | undefinedになり、引数を省略できます。

const getMessage = ({ message2 = "World", message3 }: { message2?: string, message3: string}) => {
  console.log(message2 + " " + message3)
};

getMessage({message3: "world"}) // => "World world"

2023/1/3 追記

最近、デフォルト引数って、デフォルトの実引数って意味だなと理解しました。

参考記事

デフォルト引数 (default parameter) | TypeScript入門『サバイバルTypeScript』

Rubyのメソッドの引数でデフォルト式を与える場合は、通常の引数ではなく「キーワード引数」を使おう - Qiita

オプション引数 (optional parameter) | TypeScript入門『サバイバルTypeScript』

Rubyメソッドの引数まとめ | Enjoy IT Life

分割代入引数 (destructuring assignment parameter) | TypeScript入門『サバイバルTypeScript』

キーワード引数とOptions Objectパターン | TypeScript入門『サバイバルTypeScript』