DEV Community

kaede
kaede

Posted on • Edited on

サバイバル TypeScript 2 -- オブジェクト、ReadOnly、インデックス型の使い方

https://typescriptbook.jp/reference/values-types-variables/object

なぜやるのか

業務で TypeScript を扱うプロジェクトに入り、オブジェクト配列の型の受け渡しで苦戦し質問したところ

カエデさん TS のドキュメント基礎部分を100回読んでからこれ100周した方がいいっすよw

と言われたため。

本文

オブジェクトはオリジナル

https://typescriptbook.jp/reference/values-types-variables/object/non-primitives-are-objects

const object1 = { value: 123 };
const object2 = { value: 123 };

console.log(object1 == object2); // false
Enter fullscreen mode Exit fullscreen mode

オブジェクトは中身が同じでも別のものとして計算される。


プロパティのフィールドとメソッド

const dog = {
  name: "Pochi",
  age: 3,
}
Enter fullscreen mode Exit fullscreen mode

このように普通に key:value で変数と中身を入れるのはプロパティの中でもフィールドという

const object = {
  // キーと値に分けて書いたメソッド定義
  printHello1: function () {
    console.log("Hello");
  },
  // 短い構文を用いたメソッド定義
  printHello2() {
    console.log("Hello");
  },
};
Enter fullscreen mode Exit fullscreen mode

一方、オブジェクトのプロパティとして関数を入れると「メソッド」になる

functionName: function () {}
Enter fullscreen mode Exit fullscreen mode

のように key: value で渡すこともできるが

functionName() {}
Enter fullscreen mode Exit fullscreen mode

のように中に関数定義を普通に書くこともできる

両者は全く同じもの(プロパティの中のメソッド)として扱われる

フィールドとメソッドは正確には別物で、メソッドに null を上書きするとフィールドになり、引数を渡せなくなる。


型解釈

let box: { width: number; height: number };
Enter fullscreen mode Exit fullscreen mode

オブジェクトのプロパティは、中身を入れる前にキーと型のペアを定義できる
これを 型解釈 ( type annotation ) という
型解釈ではカンマではなくセミコロンで区切るのが推奨されている

type Box = { width: number; height: number };
let box: Box = { width: 1080, height: 720 };
Enter fullscreen mode Exit fullscreen mode

このように type を使って型を先に宣言する方法もある
これは型エイリアスという。


型エイリアス

型に名前をつけるもの。型の変数と解釈した。

type StringOrNumber = string | number;
Enter fullscreen mode Exit fullscreen mode

この例がわかり易い。string | number を入れる型の変数。

type OK = 200;
type UserObject = { id: number; name: string };
type CallbackFunction = (value: string) => boolean;
Enter fullscreen mode Exit fullscreen mode

こうやって定義できる。
こうすることで、長くなる式を変数に入れて再利用するように、型も再利用できる。


読み取り専用 ReadOnly のオブジェクトでの動作

let obj: {
  readonly foo: number;
};
Enter fullscreen mode Exit fullscreen mode

オブジェクトのプロパティの型解釈も読み取り専用で定義できる。
何も入ってない状態ですら。

obj.foo = 2
Cannot assign to 'foo' because it is a read-only property.ts(2540)
Enter fullscreen mode Exit fullscreen mode

普通にそのプロパティに数値を入れようとするとエラーになる。

obj = {foo: 3}
Enter fullscreen mode Exit fullscreen mode

しかし、{ key: value } でペアとして入れると入る。よくわからない

let obj: Readonly<{
  a: number;
  b: number;
  c: number;
}>;
Enter fullscreen mode Exit fullscreen mode

また、ReadOnly で括ることで

Cannot assign to 'a' because it is a read-only property.ts(2540)
Cannot assign to 'b' because it is a read-only property.ts(2540)
Cannot assign to 'c' because it is a read-only property.ts(2540)
Enter fullscreen mode Exit fullscreen mode

まとめてブロックできる

let school: {
  readonly headTeacher: {
    name: string;
  };
};
school.headTeacher.name = "Masa"
Enter fullscreen mode Exit fullscreen mode

このコードでは、headTeacher の校長自体は変更できないが
校長プロパティである名前は変更できてしまう。

読み取り専用のプロパティのさらに中のプロパティは読み取り専用にならない
読み取り専用は再帰的ではないということだ。

https://typescriptbook.jp/reference/values-types-variables/object/readonly-vs-const

let obj: { readonly x: number } = { x: 1 };
obj = { x: 2 }; // 許可される
Enter fullscreen mode Exit fullscreen mode

また、変数の中身を丸ごと書き換えることはできてしまう。
だからプロパティを書き換えられちゃう const の上位互換というわけでもない。


中に入るプロパティの数を自由にするインデックス型

let company: {
  [K: string]: number;
};
Enter fullscreen mode Exit fullscreen mode

このように key を具体的に指定せずに、string とだけ書くものをインデックス型という。

let person: {
  age: number;
  power: number
}
person.kinniku: 100
Enter fullscreen mode Exit fullscreen mode

普通は TS では先に定義したものしか入れられない。
この例だと

Property 'kinniku' does not exist on type '{ age: number; power: number; }'.ts(2339)

筋肉は存在しないと出てしまう

let company: {
  [K: string]: number;
};

company.age = 300
company.profit = 1_000_000_000
company.members = 30
Enter fullscreen mode Exit fullscreen mode

しかし、インデックス型だと、数値を値にとるプロパティだったらいくらでも作ることができる。

let house: Record<string, number>;

house.age = 15
house.areaM2 = 30
house.monthlyPrice = 80_000
Enter fullscreen mode Exit fullscreen mode

同じようにインデックス型を Record<> を使っても定義することができる。

Top comments (0)