TypeScriptにおける「?.」、「??」、「?:」、「||」について

社内でOJTに携わることや新人の教育をすることが増えてきたのですが、
TypeScriptについて質問が多かった内容をまとめておこうと思いました。

## TypeScriptとは
TypeScriptは、JavaScriptにオブジェクト指向プログラミングや静的型付けなどの機能を追加したプログラミング言語です。
その中でも、Null合体演算子「??」、「オプショナルチェイニング」、「非Nullアサーション」「三項演算子」など、
よく使われる演算子が存在します。
本記事では、TypeScriptにおけるこれらの演算子について、それぞれ解説していこうと思います。

## オプショナルチェイニング「?.」
「?.」は、オブジェクトのプロパティやメソッドにアクセスする際に使用されます。
通常だと、以下のようにオブジェクトのプロパティにアクセスすることができます。
```typescript
Copy code
let person = {
  name: "John",
  age: 30,
  address: {
    city: "Tokyo",
    street: "Shibuya"
  }
};
let street = person.address.street;
console.log(street); // "Shibuya"
```

ただ、もし上記の例で`address`プロパティが`undefined`であった場合、
エラーが発生してしまいます。
このような場合に、「?.」を使用することで、安全にアクセスすることができます。

```typescript
let street = person.address.street; 
console.log(street); // エラー発生

let street = person.address?.street;
console.log(street); // undefined
```
このように、「?.」を使用することで、
プロパティやメソッドにアクセスする前に、
対象がnullまたはundefinedでないことを確認する必要がなくなり、
オブジェクトがnullまたはundefinedである場合にエラーを防止することができます。

## 非Nullアサーション「!」
オブジェクトや変数がnullまたはundefinedでないことをTypeScriptに伝えるために使用します。
以下のように、変数がnullまたはundefinedである場合にエラーを防止することができます。
```typescript
interface Cat {
    name: string;
}
const catName = { name: "nekocyan" };

const outputCatName = (catName?: Cat) => {
  console.log(catName.name); //'catName' is possibly 'undefined'.
};
outputCatName(catName);
```
上記の例では、outputCatName関数の引数であるcatNameが任意になっているため、
引数が渡されなかった場合に、catNameがundefinedになる可能性があるというエラーが発生します。
しかし、引数catNameプロパティが必ず存在することが保証されている場合、
以下のように「!」演算子を使用することで、エラーを防止することができます。

```typescript
// 省略
const outputCatName = (catName?: Cat) => {
  console.log(catName!.name); //OK
};
// 省略
```

以上のように、「?.」演算子と「!」演算子は、
TypeScriptで安全かつ効率的なコーディングを行う上で非常に有用な演算子です。
## Null合体演算子「??」
「??」演算子は、左側のオペランドがnullまたはundefinedである場合に、右側の値を返します。
```typescript
let count;
console.log(count ?? 10); // 10
```
上記の例では、変数countがundefinedであるため、10が出力されます。
もし、countがnullまたはundefinedでなければ、左側の値が出力されます。

「??」演算子は、「||」演算子と似ていますが、「||」演算子は、
左側のオペランドがfalsyである場合に、右側の値を返します。
一方、「??」演算子は、左側のオペランドがnullまたはundefinedである場合に、右側の値を返します。
そのため、「??」演算子は、「||」演算子とは異なり、
左側のオペランドが0やfalseの場合でも左側の値が返されます。
以下に例を書いてみます。
```typescript
let num = 0;
let result = num ?? 10;
console.log(result); // 0
```
上記の例では、変数numに0が代入されています。
「||」演算子を使った場合、numが0であるために右側の値が返されてしまいますが、
「??」演算子を使った場合は、0が正しい値として出力されます。

## 三項演算子「?:」
三項演算子は多くの言語に存在する演算子で、Javascript自体にも存在します。
```typescript
let age = 18;
let message = age >= 20 ? "成人です" : "未成年です";
console.log(message); // "未成年です"
```
上記の例では、変数ageが18であるため、条件式がfalseとなり、右側の式が返されます。

「?:」演算子は、条件式の中に「&&」や「||」などの論理演算子を使用することができます。
以下の例を見てみましょう。
```typescript
let age = 18;
let message = age >= 20 && "成人です" || "未成年です";
console.log(message); // "未成年です"
```
上記の例では、条件式の中に「&&」演算子と「||」演算子を使用しています。
ageが18であるため、左側の式がfalsyとなり、右側の式が返されます。

## 論理演算子「||」
「||」演算子は、左側のオペランドがfalsyである場合に、右側の値を返します。以下の例を見てみましょう。
```typescript
let name = "";
let displayName = name || "Anonymous";
console.log(displayName); // "Anonymous"
```
上記の例では、変数nameが空文字列であるため、左側のオペランドがfalsyとなり、右側の値が返されます。

「||」演算子は、左側のオペランドがnullまたはundefinedである場合に、右側の値が返されることがあります。
しかし、「??」演算子を使用することで、nullまたはundefinedである場合に限り右側の値を返すことができます。

## まとめ
以上が、TypeScriptにおける演算子「?.」、「!」、「??」、「?:」、「||」解説でした。
これらの演算子は、コードを短く書けたり、可読性を高めることができます。
しかし、適切に使わない場合は、コードの意味が明確でなくなったり、バグの原因になることがあります。

例えば、「?.」演算子を連続して使用することはできますが、
過剰に使用するとコードが複雑になり、可読性が下がる可能性があります。
また、「?:」演算子をネストすることもできますが、
過剰に使用するとコードが長くなりすぎて、可読性が下がる可能性があります。

そういった注意点を踏まえた上で、適切に使い分けることが重要。
特に、Nullish coalescing operator「??」は、
TypeScriptにおいて安全なデフォルト値を設定するために頻繁に使用されるため、
覚えておくと役立つシーンが多いのかなと思います。