

JavaScript(ECMAScript)において、`Function.prototype.length` は必須パラメータの数を返します。

  * [Function.length - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/length)

ECMAScriptでオプション引数を指定するには、`arguments` もしくは「rest parameter」を使用する方法があります。  
`arguments` は class 構文等の一部構文で使用できない為、「rest parameter」を使用するのが概ね現実解となります。

[code]

    'use strict';
    function foo (...args) {
      if (args.length && typeof args[0] !== 'string') throw new TypeError(args[0] + ' is not a string');
    }
    
    function bar () {
      if (arguments.length && typeof arguments[0] !== 'string') throw new TypeError(arguments[0] + ' is not a string');
    }
    
    console.log(foo.length);  // 0
    console.log(bar.length);  // 0
    foo();
    bar();
[/code]

一方、TypeScriptではオプション引数を指定する方法に `?` がありますが、この構文を使用した場合、
**Function.prototype.length が不正値になります** 。

[code]

    // TypeScript
    function foo (string?:string) {}
    console.log(foo.length); // 1
    foo();
    
[/code]

「rest parameter」でany型を指定しつつ、各々の引数を型付き変数に代入すれば、ECMAScriptと同じ実装になりますが、
**変数定義が必須** になってしまいます。。

[code]

    // TypeScript
    function foo (...args:any) {
      const string: string = args[0];
    }
    console.log(foo.length); // 0
    foo();
    
[/code]

TypeScriptで次の条件を満たすことは可能でしょうか。

  * `Function.prototype.length` が必須パラメータの数を返す
  * 仮引数で「型指定」かつ「オプション引数指定」を行う


CC BY-SA 4.0
2022-09-19T10:12:15.173
91176
2022-09-22T14:41:50.657
2022-09-19T21:43:18.127
3054
20262
question
javascript typescript
TypeScriptでオプション引数を指定しつつ、Function#lengthに正規の値を入れる方法


トランスパイル結果から想像するに、 TypeScript の `function foo(text?: string) {}` は JavaScriptの
`function foo(text) {}` 相当になるようにデザインされているので無理ではないでしょうか。  
(引数が存在しないわけではなく、呼び出し時に省略した場合に`undefined`が代入される)

代替策としては、[仕様](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/length#%E8%A7%A3%E8%AA%AC)の

> 既定値を持つ引数が最初に登場する前までしか含みません

を利用する方法が考えられます。  
(`"target": "ES6"` (以降)の設定が必要)

[code]

    // TypeScript
    function foo(text: string | undefined = undefined) {}
    console.log(foo.length); // 0
    foo();
    
[/code]

([playground](https://www.typescriptlang.org/play?target=2#code/PTAEBUE8AcFMGUDGAnAltALgKAGYFcA7RDVAewNB1NIAoNYAPDALlAGcM0CBzUAH1CEAJrByoCsIaAC8ggiLEShASlABvAL5ZE5NqQA2sAHT7S3GlVInYPDAAtlAblAhQABlzUaTrEA))


CC BY-SA 4.0
2022-09-19T16:32:22.557
91182
2022-09-19T16:32:22.557
2808
91176
answer


### Tuple型と分割代入を使う方法

> 「rest
> parameter」でany型を指定しつつ、各々の引数を型付き変数に代入すれば、ECMAScriptと同じ実装になりますが、変数定義が必須になってしまいます。。

[Tuple
Type](https://www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types)
を使うと、`any` とせずに `...args: [string?]` とできます。さらに、分割代入を使うと変数定義は不要となります。

[code]

    function foo(...[str]: [string?]) {
        console.log(str)
    }
    
[/code]

### 引数に `undefined` を許容しない方法

> 「foo(undefined) が通ってしまう」と「foo()とfoo(undefined)を区別できない」の2つの課題があるのがネック  
>  —— [出羽和之さんの回答
> へのコメント](https://ja.stackoverflow.com/questions/91176/91182#comment103550_91182)

これは一般的なことで、上のように Rest parameters
(残余引数)を用いても同じです。どうしても回避したい場合は、[オーバーロード](https://www.typescriptlang.org/docs/handbook/2/functions.html#function-
overloads)の関数シグネチャで引数の数と型を場合ごとに明示できます。

[code]

    // 関数シグネチャ:
    function foo(): void
    function foo(str: string): void
    
    // 実装:
    // 呼び出しの型チェックは下にある実装部の関数シグネチャではなく、
    // 上の関数シグネチャで行われます。
    // よって、foo(undefined) はコンパイルエラーとなります。
    function foo(...[str]: [string?]) {
        console.log(str)
    }
    
[/code]

### 追記: 省略可能な引数の型

上で `foo(undefined)` が通るのが「一般的」としたのは、JavaScript
がそのような仕様であることはもちろんですが、その仕様に依存し利用するコードが書かれている、という意味合いです。TypeScript
の設計方針は、このようなコーディングを咎めることなく許容しつつ、厳密な null チェックも可能にするものだと思います。

参考: [Use Optional
Parameters](https://www.typescriptlang.org/docs/handbook/declaration-files/do-
s-and-don-ts.html#use-optional-parameters)
では型定義ファイルの作成という文脈ですが、オーバーロードよりもオプション引数を用いて `undefined` を許容することが推奨されています。


CC BY-SA 4.0
2022-09-19T21:41:56.503
91183
2022-09-22T14:41:50.657
2022-09-22T14:41:50.657
3054
3054
91176
answer