LoginSignup
10
1

TypeScriptのふわっとしている部分を再確認

Last updated at Posted at 2023-12-10

こんにちは。
ウェブクルー AdventCalendar 11日目の記事です。
昨日は @wc_sangen さんの「 ZIOのスタックセーフの注意点 」でした。

はじめに

型について理解している気でいてもしっかりと違いを説明できない部分が多々あるなと思い、復習がてら調べ直しまとめていこうと思います。

それぞれの比較パターン

比較パターンごとに説明していきます。

1.any型とunknown型

どちらも型としての定義が完全でないイメージですが具体的な差は以下です。`

  • any型の方がunknown型よりもさらに不明確である
  • any型はTypeScriptが型のチェックをしない

言うなればunknown型はTypeScriptが型のチェックをしてくれるため未定義のプロパティやメソッドへのアクセスは不正であるとチェックしてくれます。

// dataがunknown型の場合

console.log(data) // これはOK

console.log(data.length) // これはNG

console.log(data.pop()) // これはNG

2.typeとinterface

① typeは継承ができずinterfaceは継承ができる

これはtypeも交差型で対応できまずが、interfaceはデフォルトで備わっているためinterfaceの方が拡張性があるイメージです。

typeの交差型
type Human = {
    name: string
}

type Taro = Human && {
    glasses: boolean
}
interfaceの継承
interface Human {
    name: string
}

interface Taro extends Human {
    glasses: boolean
}

② 同名の型についてinterfaceは自動でマージされる

TypeScript側で良しなにマージしてくれます。

interfaceの場合

interface Human {
    name: string;
}

interface Human {
    age: number;
}

// 結果
interface Human {
    name: string;
    age: number;
}

typeの場合

type Human = {
    name: string;
}

type Human = {
    age: number;
}

// 結果 エラーとなる

interfaceでも無理な場合

interface Human {
    name: string;
}

interface Human {
    name: number;
}

// 結果 エラーとなる

③typeではMapped Typesが使用可能

ここは私自身知らなかったのですが以下です。
typeの場合にはオブジェクトのキーに対してユニオン型の指定ができますが interfaceでは使用できません。

type JsFramework = "Vue" | "React" | "Angular";
type PossibleFramework = {
  [key in JsFramework]: string;
};

interfaceでも無理な場合でもあるように整合性がない上書きではエラーとなります。
結論interfaceの方が拡張性には富んでいますが、チーム開発で使用する場合には注意が必要です。

3.Objectの型定義

プロパティが未知の場合の定義としてはObject,object,{}があります。
それぞれの違いについて記載していきます。

object Object {}
空のオブジェクトを許容する
プリミティブ型(null, undefined)の代入を許容する × ×

というように一番は具体的なプロパティ単位まで細かく記載がベターですが、それが無理な場合にはobject型で定義するのが良さそうですね。

4.ラッパーオブジェクトとプリミティブ型の定義

上と少し関連ある部分ですが、今回はプリミティブ型です。
プリミティブ型には本来フィールドやメソッドはないですが、JavaScript側で良しなにメソッドやフィールドを呼び出せるようにしてくれています。そのことを自動ボックス化と言います。その際にラッパーオブジェクト化してくれています。
これをJavaScript側に任せずに意図的にやろうとすると下記です。


// プリミティブ型の場合
let count:number = 1;

// ラッパーオブジェクトで定義する

let count:Number = 1;
// or
const initial = 1;
let count = new Number(initial);

プリミティブ型とラッパーオブジェクト型の違いは下記です。

  • プリミティブ型をラッパーオブジェクト型で定義することは可能だが、逆は不可
  • 演算子が使用できない

というように上記からあまりラッパーオブジェクト型で定義する利点はあまりなさそうですね。

最後に

今までふわっとしていた部分を再度理解し直すことができました。
やはり解決策を調べるだけでなく、なぜそうなのかと根本から理解し直すことが一番知識に定着しますね。

明日は@tatsuyanamikiの投稿になります。お楽しみに!

10
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
1