お前らのTypescriptはundefinedしすぎ

# はじめに

僕も[お前らのXXXXは<ネガティブな形容詞>シリーズ](https://qiita.com/search?utf8=%E2%9C%93&sort=&q=title%3A%E3%81%8A%E5%89%8D%E3%82%89%E3%81%AE) をマネしたかったんです。
ごめんなさい。

Typescript書いていますか？

あなたはundefined派ですか？
null+undefined派ですか？

throw new Error派ですか？
return {error:null, result:true}派ですか？

nullとundefinedがないコードを書いてみましょう。



## なんでnullを使ったらいけないの？

例えば、こういう例

```typescript:nullExample.ts
//初期値としてnullを使う
let str:string = null;

//何か処理をしてstrに格納...できてなかった

console.log(str.toLowerCase()); //TypeError: Cannot read property 'toLowerCase' of null
```

nullがハンドリングできていないので`toLowerCase()`でエラーになります。

tsconfig.jsonに `"strict":true` や `"strictNullChecks":true`をつけておくと、この例はコンパイルが通らないので、まず有効にしましょう。


`null`ではなく、`undefined`にすると、コンパイラが警告してくれますし、Optional chainingなどもできるようになります。


```typescript:compaileFailed.ts
let str:string|undefined = undefined;
console.log(str.toLowerCase()); //コンパイルエラー strは必ずundefinedだから
```

```typescript:optionalChainingExample.ts
let str:string|undefined = undefined;

if(Math.random() > 0.5){ //特に意味のない、コンパイルを通すための処理
    str = 'yey';
}

console.log(str?.toLowerCase()); //strがundefinedだったら、toLowerCase()が実行されない
```




## でも、`null` or `undefined`は統一されていない

```typescript:someReturnPatterns.ts
document.querySelector("abc"); //null

[1,2,3].find(v=>v===4); //undefined

[1,2,3].filter(v=>v===4); //[]
```
綺麗事だけじゃ生きていけない。


## `undefined`に統一してみる・・・こんな場合はどうする？

```typescript:index.ts
//ダウンロードしたファイルの中身を文字列で返す。
function downloadSomething(url:string): string | undefined{

    //ダウンロード失敗したらundefinedを返す？
    //複数のエラーの切り分けをどうする？
    //throw new Errorして外側のtry-catchでハンドリングする？

}
```
エラーハンドリングをどうするか、困ることが多いのではないでしょうか。
このように書く方法もあります。

```typescript:errorHandlingSample.ts
function validate(value: number): {error?: string} {
  if (value < 0 || value > 100) return {error:'Invalid value'};
}
```
[↑Typescript deep diveでは、こういう書き方を紹介しています。](https://typescript-jp.gitbook.io/deep-dive/type-system/exceptions#shisutemudeumakusareteinai)


でも、戻り値が必要だと `{error?: string, result?:string}`のようになってきて、
チームが大きいと統一が難しくなってきます。



## RustのOptionとResultはいいぞ

Rustでは、言語としてこういった問題に対処するためのデータ構造を用意しています。

- `Option`は`Some<T>`か`None`という値を取る
    - `null`や`undefined`を使わないコードが書ける
- `Result`は`Ok<T>`か`Err<E>`という値を取る
    - 失敗する可能性がある関数の戻り値をハンドリングできる

## Rustの`Option`と`Result`をTypescriptにポーティングしました。
[https://github.com/komasayuki/ts-rust](https://github.com/komasayuki/ts-rust)


[Typescript deep diveの ”ひどい関数の例”](https://typescript-jp.gitbook.io/deep-dive/recap/null-undefined#validitynodeundefinedwoshinai)で考えてみます。

```typescript:badFunctionExample.ts
function toInt(str:string) {
  return str ? parseInt(str) : undefined;
}

```
引数の`str`は`null`を取る可能性がある。
これを[ts-rust](https://github.com/komasayuki/ts-rust)を使ってRustっぽく書くには

`toInt(str:string)` を `toInt(str:Option<string>)`と宣言する。

また、戻り値は`undefined`のケースがあります。
そのため、元の戻り値は `number|undefined`でした。

この戻り値は`Result<int, string>`と宣言します。
書き直すと

```typescript:tsRustExample.ts
import {RustOption as Option, RustResult as Result} from 'ts-rust'

//簡単な例
function toIntVersion1(str:Option<string>) :Result<number, string> {
    if(str.isSome()){
        return Result.Ok(str.unwrap());
    }
    return Result.Err('parse failed');
}

//よりRustっぽい例
function toIntVersion2(str:Option<string>) :Result<number, string> {
    //str.map(parseInt)     strがSomeならparseIntに渡す、strがNoneならそのままNoneを返す
    //okOr()                左辺がOption.SomeならResult.Ok(左辺)を返す、左辺がOption.NoneならResult.Err(引数)を返す
    return str.map(parseInt).okOr('parse failed');
}

console.log(toIntVersion2(Option.Some(234)).toString()); //Ok(234)
console.log(toIntVersion2(Option.None)+''); //Err("parse failed")
```

`toIntVersion1`や`toIntVersion2`のようになります。

※最初の例から、そもそもparseIntのNaNをハンドリングしていないことは無視して下さい！



## 新しく作らなくてもNullableな型は提供してるライブラリはあるよね

あったんですが、いくつかの理由で作り直すことにしました。

- RustのOption/Resultの全機能を素直にポーティングしているものがなかった
- Rustはsnake_caseだけど、TypescriptではcamelCaseにしたかった
- デバッグするときに優しくなかった
- 重要なベースになるライブラリなので、外部依存のない小さいライブラリにしたかった


## Rustはいいぞ
- Typescriptもいいぞ