edy hub

プログラミングやライフスタイルについて書き綴っています

Typescript学習〜『プロを目指す人のためのTypeScript入門』を読んでみて第3章〜

はじめに

業務で使うことのあるTypeScriptを本格的に勉強し始めたところです。

現在絶賛『プロを目指す人のためのTypeScript入門』を読み進めており、その際のメモを備忘録として残しております。

前回の記事 www.edyhub.com

書籍はこちら

第3章

第3章は「オブジェクトの基本とオブジェクトの型」について。

オブジェクトとは

TypeScriptのオブジェクトは、連想配列ですと。

ハッシュや辞書などとも呼ばれますが、ここでは「いくつかの値をまとめたデータ」としておきます。

この世界は先の章で扱った文字列、数値などのプリミティブ型だけで表現できるほど単純には出来ていませんよね。

ひとりのユーザーを表現したい場合にも「名前」「年齢」「画像」「住所」など関連するデータは多様に存在します。

これら複数のデータをまとめてユーザーを表現したいときにオブジェクトという単位でまとめることが必要になります。

※とはいえ実運用を加味すると「ユーザー」というオブジェクトだけでシンプルに運用するには限界が訪れて、「プロフィール」オブジェクトなど更に階層構造が生まれていくと思います。

オブジェクトの省略記法

オブジェクトリテラルには、省略記法があります。

例えば、以下のオブジェクトの代入を例にあげます。

最終的にユーザーの姓名を足して、ユーザー名として出力しています。

const family_name = '田中'
const given_name = '太郎'

const user = {
  family_name: family_name,
  given_name: given_name
}

const user_name = family_name + given_name
console.log(`ユーザー名: ${user_name}`)
 

今回取り上げる「省略記法」というのは、プロパティ名と変数名が同一の場合に活用できます。

変数定義で family_namefirst_name が用いられていますが、userオブジェクト内でも同様のプロパティが出現しています。

その場合、コーディングの効率を上げるために、以下のように書き換えることが出来ます。

const family_name = '田中'
const given_name = '太郎'

const user = {
  family_name,
  given_name
}

const user_name = family_name + given_name
console.log(`ユーザー名: ${user_name}`)

userオブジェクトの family_name: family_name given_name: given_name がそれぞれ対応する変数名だけで表現できるようになりました。

コンパクトな記述ができるため、利用できる際にはどんどん活用していけると良いですね。

スプレッド構文

オブジェクトリテラル中ではスプレッド構文と呼ばれる別オブジェクトからプロパティをコピーできる構文を利用できます。 既存のオブジェクトを拡張した別オブジェクトを作りたいときに有効に活用できます。

const userName = {
  family_name: '田中',
  given_name: '太郎',
}

const userProfile = {
  age: 20,
  ...userName
}

console.log(userProfile)

// 結果
// { age: 20, family_name: '田中', given_name: '太郎' }

注意点として、プロパティをコピーしているため、コピー元のオブジェクト側のプロパティ変更は反映されません。 一方、コピー元のネストしたプロパティに関しては依然として同一オブジェクトとみなされます。 オブジェクトのコピーと同一性は正しく理解しないと思わぬ破壊的変更を生む可能性があるので注意しましょう。

const foo = {
  obj: { num: 1234 }
}
const bar = { ...foo }
bar.obj.num = 0
console.log(foo.obj.num)

// 0 が返る

type文

TypeScript特有でJavaScriptには存在しない文としてtype文があります。

これは型名を宣言をする構文です。

type 型名 = 型;

まずtype文でFooBarObjという型名を作成します。続けて行う変数宣言では作成したFooBarObj型でオブジェクトを定義します。 それを表したのが以下のコードです。

type FooBarObj = {
  foo: string;
  bar: number;
};
const obj: FooBarObj = {
  foo: 'Hello, World',
  bar: 2022,
}

console.log(obj);

// { foo: 'Hello, World', bar: 2022 }

ちなみに、typeによる型名作成は interface宣言 でも可能です。

interface FooBarObj {
  foo: string;
  bar: number;
};
const obj: FooBarObj = {
  foo: 'Hello, World',
  bar: 2022,
}

console.log(obj);

// typeの場合と結果は変わらない
// { foo: 'Hello, World', bar: 2022 }

※書籍の筆者の見解はtype文のみを使用すれば困ることはないという意見

部分型関係

  • 部分型は2つの型の互換性を表す概念
  • 型Sが型Tの部分型であることは、S型の値がT型の値でもあることを示す
  • TypeScriptは構造的部分型(プロパティの実際の比較により部分型かどうか決定される)
  • 他言語は名前的部分型(明示的に宣言されて初めて部分型になる)
  • TypeScriptにも名前的部分型が導入されることは要望として上がっているが現状は仕組みとして存在しない

型引数(type parameters)

型定義の際に、パラメータを伴って型定義の幅を増やしてくれるのが「型引数」です。 型引数を持つ型はジェネリック型とも呼ばれます。

例えば型引数ParentとChildを持つ Family<Parent, Child> を宣言すると以下のようになります。

ジェネリック型のFamily<Parent, Child>

type Family<Parent, Child> = {
  mother: Parent;
  father: Parent;
  son: Child;
  daughter: Child;
}

配列型

プログラミングをやっていれば頻出の配列(Array)に対しても型の構文が存在しています。

例えば、 number[] という型は number 型の値を要素に持つ配列を表現します。

以下の例だとarr1はOKですが、arr2はコンパイルエラーになります。

const arr1: number[] = [1, 10, 100];

const arr2: string[] = [1, 10, 100];

まとめ

第3章も内容は「オブジェクトとは?」についてでした。

オブジェクト