TypeScript Handbook を読む (22. Type Checking JavaScript Files)

[TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/basic-types.html) を読み進めていく第二十二回目。

1. [Basic Types](http://qiita.com/murank/items/9c1f633545842c953947)
1. [Variable Declarations](http://qiita.com/murank/items/913f1c1e4e725047436c)
1. [Interfaces](http://qiita.com/murank/items/553276c3dc2eec9bfe47)
1. [Classes](http://qiita.com/murank/items/7e8f15225bc8259e9bf3)
1. [Functions](http://qiita.com/murank/items/e310ce68ed5a0058d42d)
1. [Generics](http://qiita.com/murank/items/144b686d34fd433a97a3)
1. [Enums](http://qiita.com/murank/items/cc487562b21bae3728ba)
1. [Type Inference](http://qiita.com/murank/items/e67ad7426020321228e9)
1. [Type Compatibility](http://qiita.com/murank/items/e06efb0615294a6826e3)
1. [Advanced Types](http://qiita.com/murank/items/bc1776efbecbd2120d94)
1. [Symbols](http://qiita.com/murank/items/04b3b31caa677750f240)
1. [Iterators and Generators](http://qiita.com/murank/items/7ceec53dde886f31a682)
1. [Modules](http://qiita.com/murank/items/0fc4e059be8ea5e1c274)
1. [Namwspaces](http://qiita.com/murank/items/0dfad4a0bb91aaa6c97b)
1. [Namespaces and Modules](http://qiita.com/murank/items/0be1978a3515fc76e7ba)
1. [Module Resolution](http://qiita.com/murank/items/5f11466474d49498bbd5)
1. [Declaration Merging](http://qiita.com/murank/items/ec90ba04edab1ea3718e)
1. [JSX](http://qiita.com/murank/items/90164aa9a3bed3c49a80)
1. [Decorators](http://qiita.com/murank/items/580e34883e0832ca7aed)
1. [Mixins](http://qiita.com/murank/items/e77ad8359ed1fd82822d)
1. [Triple-Slash Directives](http://qiita.com/murank/items/faff57883338ebdeda12)
1. Type Checking JavaScript Files (今ココ)


# Type Checking JavaScript Files
[原文](https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html)

TypeScript 2.3 から `--checkJs` を指定することで `.js` ファイルの型チェックを行えるようになりました。

`// @ts-nocheck` コメントを追加することで、特定のファイルだけチェックをスキップすることができます。
逆に、`--checkJs` を指定せずに `// @ts-check` コメントを追加することで、特定の `.js` ファイルのみ、チェックを行うことも可能です。
また、特定の行だけエラーを無視したい場合、その前の行に `// @ts-ignore` を追加してください。


## Using types in JSDoc
`.js` ファイルにおいても、`.ts` ファイルと同様に型の推論が行われます。
型を推論できない場合、JSDoc を使用して `.ts` ファイルの型アノテーションと同じ方法で型を指定します。

変数宣言に付けられた JSDoc アノテーションは宣言の型を指定するために使用されます。

```js:JavaScript
/** @type {number} */
var x;

x = 0;      // OK
x = false;  // エラー: boolean は number に代入できない
```

サポートされている JSDoc の一覧は [JSDoc サポート](https://github.com/Microsoft/TypeScript/wiki/JSDoc-support-in-JavaScript) を参照してください。


## Property declaration inferred from assignments in class bodies
ES2015/ES6 にはクラスのプロパティを宣言する方法がなく、プロパティは動的に代入されます。

`.js` ファイルでは、クラスボディ内でのプロパティへの代入を基に、プロパティ宣言を推測します。
プロパティの型はすべての代入文の右辺に指定された値の型の共用型になります。
コンストラクタ内で定義されたプロパティは常に存在するとみなされ、メソッド、ゲッター、セッター内で定義されたプロパティは任意のプロパティとみなされます。

JSDoc で代入文を修飾することでプロパティの型を指定することも可能です。

```js:JavaScript
class C {
    constructor() {
        /** @type {number | undefined} */
        this.prop = undefined;
    }
}


let c = new C();
c.prop = 0;         // OK
c.prop = "string";  // エラー: 文字列は number|undefined に代入できない
```

クラスボディ内で一度も代入されないプロパティは不明とみなされます。
もしも読み取りしか行わないプロパティがある場合、コンストラクタで undefined に初期化することを検討してください。(例: `this.prop = undefined;`)
>「不明」扱いになった時には何が起きるんだろう? 

## CommonJS module input support
`.js` ファイルでは CommonJS モジュール形式に対応しています。
`module.exports` がエクスポート宣言として認識される他、`require` 関数がモジュールのインポート宣言として認識されます。

```js:JavaScript
// "fs" モジュールのインポート
const fs = require("fs");


// readFile 関数のエクスポート
module.exports.readFile = function(f) {
    return fs.readFileSync(f);
}
```

## Object literals are open-ended
デフォルトでは変数宣言時のオブジェクトリテラルが変数の型となり、初期化時に指定していなかったメンバを新たに追加することはできません。
`.js` ファイルではこのルールが緩められており、オブジェクトリテラルは変更可能な型として扱われます。
つまり、元々定義されていないメンバを追加・参照することが可能です。

```js:JavaScript
var obj = { a: 1 };
obj.b = 2;  // 許可されている
```

このように変更可能なオブジェクトとして扱うため、オブジェクトリテラルはデフォルトでインデックスシグネチャ `[x:string]: any` を持つようになります。

他の JS チェックの振る舞いと同じように、変数に JSDoc `type` を付与することで振るまいを変更することが可能です。

```js:JavaScript
/** @type  */
var obj = { a: 1 };
obj.b = 2;  // エラー, {a: number} 型はプロパティ b を持たない
```

## Function parameters are optional by default
JavaScript では (デフォルト引数を指定することなしに) 引数を任意にする方法がないため、`.js` ファイルでは関数の引数はすべて任意とみなされます。
つまり、少ない引数での呼び出しが許可されているということです。

一方で、引数が多すぎる場合はエラーになるということを覚えておいてください。

```js:JavaScript
function bar(a, b){
    console.log(a + " " + b);
}

bar(1);       // OK、第二引数は任意とみなされている
bar(1, 2);
bar(1, 2, 3); // エラー、引数が多すぎる
```

関数に JSDoc アノテーションを付与することでこの振る舞いを変更可能です。
JSDoc の optional parameter 構文を使用して任意の引数を表現してください。

```js:JavaScript
/**
 * @param {string} [somebody] - Somebody's name.
 */
function sayHello(somebody) {
    if (!somebody) {
        somebody = 'John Doe';
    }
    alert('Hello ' + somebody);
}

sayHello();
```
>任意の引数は引数名を `[]` で囲むようだ


## Var-args parameter declaration inferred from use of `arguments`
関数内で `arguments` を参照している場合、その関数は可変数引数を受け付ける (つまり `(...arg: any[]) => any`) とみなされます。
引数の型を指定する場合は JSDoc の var-arg 構文を使用してください。


## Unspecified type parameters default to `any`
ジェネリクスの型パラメータが指定されていない場合、デフォルトで `any` となります。

### In extends clause
例えば、`React.Component` は `Props` と `State` というふたつの型パラメータを定義していますが、`.js` ファイルでは extends 句の中でこれらの型パラメータを指定する方法はありません。
そのため、デフォルトで `any` となります。

```js:JavaScript
import { Component } from "react";

class MyComponent extends Component {
    render() {
       this.props.b; // this.props は any 型となるため、許可されている
    }
}
```

型を明示する場合は JSDoc `@augments` を使用してください。

```js:JavaScript
import { Component } from "react";

/**
 * @augments {Component<{a: number}, State>}
 */
class MyComponent extends Component {
    render() {
        this.props.b; // エラー: b は {a:number} に存在しない
    }
}
```

### In JSDoc references
JSDoc 内でジェネリクスの型パラメータが指定されていない場合、デフォルトで `any` となります。

```js:JavaScript
/** @type{Array} */
var x = [];

x.push(1);        // OK
x.push("string"); // OK、x は Array<any> 型


/** @type{Array.<number>} */
var y = [];

y.push(1);        // OK
y.push("string"); // エラー、文字列は number に代入できない
```

### In function calls
ジェネリック関数の呼び出し時には、引数を基に型パラメータを推論します。
しかし、型を推論するための情報が不足しているなどの理由から、推論に失敗することがあります。
この場合、型パラメータはデフォルトで `any` となります。

```js:JavaScript
var p = new Promise((resolve, reject) => { reject() });

p; // Promise<any>;
```
> `reject()` が何を返すか分からないから `any` にせざるを得ない、というわけだ