サバイバル タイプスクリプトオブジェクトの扱い方


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

なぜやるのか


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

カエデさん 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
オブジェクトは中身が同じでも別のものとして計算される.

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


const dog = {
  name: "Pochi",
  age: 3,
}
このように普通に キーバリューで変数と中身を入れるのはプロパティの中でもフィールドという
const object = {
  // キーと値に分けて書いたメソッド定義
  printHello1: function () {
    console.log("Hello");
  },
  // 短い構文を用いたメソッド定義
  printHello2() {
    console.log("Hello");
  },
};
一方、オブジェクトのプロパティとして関数を入れると集約メソッド」になる
functionName: function () {}
のように キーバリューで渡すこともできるが
functionName() {}
のように中に関数定義を普通に書くこともできる
両者は全く同じもの(プロパティの中のメソッド)として扱われる
フィールドとメソッドは正確には別物で、メソッドに NULL を上書きするとフィールドになり、引数を渡せなくなる.

型解釈


let box: { width: number; height: number };
オブジェクトのプロパティは、中身を入れる前にキーと型のペアを定義できる
これを 型解釈 ( type annotation )という
型解釈ではカンマではなくセミコロンで区切るのが推奨されている
type Box = { width: number; height: number };
let box: Box = { width: 1080, height: 720 };
このように 種類を使って型を先に宣言する方法もある
これは型エイリアスという.

型エイリアス


型に名前をつけるもの.型の変数と解釈した.
type StringOrNumber = string | number;
この例がわかり易い. string | numberを入れる型の変数.
type OK = 200;
type UserObject = { id: number; name: string };
type CallbackFunction = (value: string) => boolean;
こうやって定義できる.
こうすることで、長くなる式を変数に入れて再利用するように、型も再利用できる.

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


let obj: {
  readonly foo: number;
};
オブジェクトのプロパティの型解釈も読み取り専用で定義できる.
何も入ってない状態ですら.
obj.foo = 2
Cannot assign to 'foo' because it is a read-only property.ts(2540)
普通にそのプロパティに数値を入れようとするとエラーになる.
obj = {foo: 3}
しかし、{ key : value }でペアとして入れると入る.よくわからない
let obj: Readonly<{
  a: number;
  b: number;
  c: number;
}>;
また、読み込み専用で括ることで
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)
まとめてブロックできる
let school: {
  readonly headTeacher: {
    name: string;
  };
};
school.headTeacher.name = "Masa"
このコードでは、校長の校長自体は変更できないが
校長プロパティである名前は変更できてしまう.
読み取り専用のプロパティのさらに中のプロパティは読み取り専用にならない
読み取り専用は再帰的ではないということだ.
https://typescriptbook.jp/reference/values-types-variables/object/readonly-vs-const
let obj: { readonly x: number } = { x: 1 };
obj = { x: 2 }; // 許可される
また、変数の中身を丸ごと書き換えることはできてしまう.
だからプロパティを書き換えられちゃう コンストの上位互換というわけでもない.

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


let company: {
  [K: string]: number;
};
このように キーを具体的に指定せずに、文字列とだけ書くものをインデックス型という.
let person: {
  age: number;
  power: number
}
person.kinniku: 100
普通は 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
しかし、インデックス型だと、数値を値にとるプロパティだったらいくらでも作ることができる.
let house: Record<string, number>;

house.age = 15
house.areaM2 = 30
house.monthlyPrice = 80_000
同じようにインデックス型を レコード<>を使っても定義することができる.