頑張らないために頑張る

ゆるく頑張ります

TypeScriptの関数で引数をいろいろする方法

Posted at — May 4, 2021

いろいろするって雑すぎるだろ

いい表現方法が思い浮かばなかったんだよぅ。

たとえば

基本的な関数における引数の指定方法は、引数名:引数のデータ型という感じで宣言します。

const func = (str: string):string => 'input: ' + str;

console.log(func('hoge'));

これが基本形。ここでは、これをもう少し便利に使える記述方法だったり、拡張機能を見ていこうと思います。

関数の引数にオブジェクトを指定する

当たり前ですが、関数の引数は文字列や数値などのプリミティブな型以外に、オブジェクトを指定できます。

const func = (arg:{x: string, y: string, z: string}): string => arg.x + arg.y + arg.z;

const func2 = (arg:{x: number, y: number, z: string}, arg2: string) => {
    return arg.x.toString() + arg.y.toString() + arg.z + arg2;
}

console.log(func({x:'foo', y: 'bar', z: 'baz'}));

console.log(func2({x: 1, y: 2, z: 'funcfunc'}, 'fugafuga'));

記述方法は上記のとおりで、引数のオブジェクト自体に名前をつけて、各プロパティにそれぞれ型情報を付与してやります。関数内で引数を参照する場合は、オブジェクトの名前.プロパティ名で参照できます。基本的には、普段のTypeScriptの書き方で問題ありません。

type Hoge = {
    foo: number[];
    bar: string;
    baz: Function;
};

const sum = (numbers: number[]) => numbers.reduce((total, value) => total + value);

const func = (hoge: Hoge) => {
    console.log(sum(hoge.foo));
    console.log(hoge.bar.substr(3, 5));
    console.log(hoge.baz('hogehoge'));
}

let hoge:Hoge = {
    foo: [1, 2, 3, 4, 5],
    bar: 'hogefugapiyo',
    baz: (arg: string) => arg + ' in func'
}

func(hoge);

上記のように、typeで定義したオブジェクトを引数に指定することも可能です。また、interfaceを使った記述方法も同様に可能です。

ちなみに上記のsum()関数は、数値の配列の要素を合算します。意外と、要素を合算するような配列のメソッドとかはないっぽい。

関数の引数をオプションにする

関数宣言時に引数を単純に指定すると、その引数は指定が必須になります。引数の指定を任意にしたい場合は、その引数名に?を付与することが必要です。

const f = (arg: string) => {
    return arg;
}

const f2 = (arg? : string) => {
    return arg;
}

const f3 = (arg1: string, arg2?: string, arg3?: string) => {
    return arg1 + arg2 + arg3;
}

console.log(f('foo'));
console.log(f2('bar'));
console.log(f3('ham', 'eggs', 'spam'));

console.log(f3('ham'));
console.log(f3('ham', 'eggs'));

console.log(f()); // error Expected 1 arguments, but got 0.
console.log(f2());
console.log(f3()); // error "Expected 1-3 arguments, but got 0."

関数fのように引数名に?がない場合、引数を省略し実行しようとしてもエラーになります。しかし、引数名?とすることでその引数をオプション扱いにできるので、引数を省略して関数を実行することが可能です。

オプションの引数と必須の引数を、1つの関数に同居させることもできます。この場合、必須の引数が存在するため引数を完全に省略して関数を実行するのは不可能だが、オプションの引数は省略が可能です。

関数の引数にデフォルト値を設定する

関数の引数にはデフォルト値を指定できます。関数実行時に引数が指定されなかった場合は、デフォルト値が採用され関数を実行できます。

const f = (arg: string = 'hoge') => {
    return arg;
};

console.log(f('foo'));
console.log(f());

console.log(f(''));

引数が指定されなかった場合、デフォルト値である「hoge」という文字列が採用されます。

"foo" 
"hoge" 
"" 

実行結果は上記のとおりです。

ちなみに、引数にstring形式を指定した場合で空文字を渡すのは、引数に設定なしとは見なされず空文字が正規の指定になるので一応注意が必要です。

const f2 = (arg1: number = 1, arg2?: number = 2) => { // error "Parameter cannot have question mark and initializer."
    return arg1 + arg2;
}

なお、前述の引数をオプションにする?とデフォルト値の設定は同時指定できません。「デフォルト値を設定するくらいなんだから、その引数は必須なんでしょ?オプション扱いしたらデフォルトに指定した値どうするのよ」ということらしいです。まぁそうだわな。

Rest引数を使う

TypeScriptにはRest引数というものがあります。これは引数を可変長にできる機能で、ある引数名の...を指定し配列として宣言することで実装可能です。

const f = (arg1: string, ...arg2: number[]) => {
    let sum: number = arg2.reduce((total, value) => total += value);

    return arg1 + ': ' + sum.toString();
};

console.log(f('hoge', 1));
console.log(f('hoge', 1, 2, 3));
console.log(f('hoge', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));

const f2 = (...arg: string[]) => {
    arg.forEach(item => {
        console.log(item);
    });
};

f2('ham', 'eggs', 'spam');

...arg2が可変長である宣言に該当し、...arg2: number[]と記述することでnumber型の配列として宣言します。これにより、引数を可変に指定できます。関数内では、可変長の引数は配列として扱うのでforEachなどで逐次処理が可能です。

なお、上記の例ではarg1は必須の引数扱いなので、こっちは指定が必要です。

"hoge: 1" 
"hoge: 6" 
"hoge: 55" 
"ham" 
"eggs" 
"spam" 

関数実行時、引数を指定したいだけ指定すればよいので、「配列化してから引数として渡す」などの前処理が必要ないのは嬉しいところ。データ型はわかっているけど、データ数が確定しない場合などに威力を発揮します。

ただ、引数として渡したい配列に含まれるデータ型がstringnumberのごちゃまぜ、という場合などはこれまでの方法だとうまくいきません。そこで別の方法を使います。

const f = (...arg: Array<number | string | boolean>) => {
    arg.forEach(item => console.log(item));
}

f(1, 'hoge', 2, true, 3, 'fuga', 'piyo', false);

可変長の引数に複数のデータ型を指定したい場合、Array<T>の形式で指定します。

const f = (...arg: Array<number | string>) => {
    arg.forEach(item => console.log(item));
}

f('foo', 'bar', 1, 'baz', 2, 3);

let foo = [1, 2, 'ham', 3, 'eggs', 'spam'];

f(...foo);

const hoge = [1];
const fuga = [4, 5];

const f2 = (...arg: Array<number>) => {
    let sum = arg.reduce((total, value) => total += value);
    return sum;
}

console.log(f2(...hoge));
console.log(f2(...fuga));

上記のように、引数が文字列と数値のごちゃまぜであったとしても、ちゃんとデータ型を指定してやることで1つの配列として処理できます。

なお、「もともと存在する配列を可変長の引数扱いしたい」という場合は、関数実行時に配列をスプレッド構文とともに指定してやることで、配列を展開しつつ引数としての指定が可能です。

const f2 = (...arg?: string[]) => { // error "A rest parameter cannot be optional."
    return arg;
}

ちなみに、Rest引数もオプション扱いできないので注意。

まとめ

TypeScriptで引数を便利に使える方法をいくつか見てみました。オプション指定なんかは普段遣いすることも多いと思いますが、Rest引数なんかも上手に使えるととても有用なので適材適所で使っていきたいものです。

参考

  1. More on functions
  2. TypeScript プログラミング -関数 -引数
  3. Utility Types
  4. Object Types
  5. TypeScriptの関数を振り返る
comments powered by Disqus