object 型

オブジェクトな値なら何でも入れることができる型です。
型にはobjectを使います。

const obj: object = {};

構造化したオブジェクトを Interface で定義する

ある程度構造化したobjectが使いたいなら、interfaceで型を作ります。以下はfoobarというプロパティを持つValue型の例です。

interface Value {
  foo: number;
  bar: number;
}

const value: Value = {
  foo: 0,
  bar: 1
};

ちなみに、以下はbazは持っていないプロパティなのでエラーになります。結構厳密です。

const value: Value = {
  foo: 0,
  bar: 1,
  baz: 2,
};

bazが静的であればValue型にbazを追加することで直せます。また動的な場合はインデックスシグネチャを型に追加してあげます。

interface Value {
  [key:string]: number;
  foo: number;
  bar: number;
}

Interface で構造化したもののプロパティを変数からアクセスできるようにする

例えば'foo'はどこからからやってきた変数propに入っている値だとして、value[prop]で取れそうですがこれはエラーになります。これにはインデックス署名をその Interface に追加する必要があります。

value[prop]で値を取れるようにしたものが以下にあります。[index: string]: number | string部分がその問題の部分です。これはstringが来た時にfoobarの値どちらかが取得できるということを表しています。それはfoobarプロパティがある為で、値の型は取得できるうるすべてをUnion化したものにする必要があります。。

interface Value {
  [index: string]: number | string;
  foo: number;
  bar: string;
}

また[index: string]部分は[index: number]とも定義できます。これはvalue[number]のようなアクセスの為に必要です。

Interface のプロパティを Partial 化する

1部だけ

Partial Propertyとは、あってもなくてもどちらでも構わないというようなプロパティのことです。これはプロパティ名の後:の代わりに?:とするだけです。

試しにbarを Partial 化するとこうなります。無くてもエラーになりません。

interface Value {
  foo: number;
  bar?: number;
}

const value: Value = {
  foo: 'foo'
}

すべてのプロパティを Partial 化

すべてを Partial 化したいなら全てに一々?を付ける必要はありません。Partialという TypeScript 自身が提供している Generics 化を使うことですべてを Partial 化できます。

interface Value {
  foo: number;
  bar: number;
}

type PartialValue = Partial<Value>;

このpartialValueは、次のように書くのと同じ意味になります。

interface PartialValue {
  foo?: number;
  bar?: number;
}

Record 型で一気に定義する

すべてのプロパティが同じ型の時便利なのがRecord<P extends string, V>型です。Pは、UnionstringVはそれぞれプロパティとなる値を設定します。つまり、P = 'foo' | 'bar'V = numberの場合、それは{foo: number, bar: number}となります。
つまり、次の2つはまったく同じ型になります。

type Value = Record<'foo' | 'bar', number>;
interface Value {
  foo: number;
  bar: number;
}

Interface の extends を使ってある構造を継承した新たな構造を作る

以下はValueのプロパティにbarを増やしたものになります。extendsで継承しているのでfoobarの記述はいりません。

interface ValueWithBaz extends Value {
  baz: number;
}
// 同じ
interface ValueWithBaz {
  foo: number;
  bar: number;
  baz: number;
}

Interface の単純分割

同じ名前でインターフェースを定義すると勝手にtype A = Foo & {children: unknown[]}のようにインターセプトしたタイプのように設定することができます。

interface A {
  foo: string
}

interface A {
  children: unknown[];
}

const a: A = {
  foo: 'foo',
  children: []
};

厳密なオブジェクトにする

例えば以下の時、

const a = {
  foo: 'foo',
  bar: 123,
  baz: true
};

これはこのような型が自動で付きます。

{
  foo: string;
  bar: number;
  baz: boolean
}

これは無いよりはマシですが、定数や型の特定に使えるような値がほしい時これではタイプガード系の関数を定義したり、キャストが必要になってしまいます。

そんな場合はオブジェクト宣言の後ろにas constと付けてあげます。(定数であればObject.freezeでJavaScriptから見た時でも凍結されているようにするとより良いと思います)

const a = {
  foo: 'foo',
  bar: 123,
  baz: true
} as const;

すると型は、

{
  readonly foo: 'foo';
  readonly bar: 123;
  readonly baz: true
}

とすることができます。これにより上書き防止(無いよりマシ)や、ifで型の絞り込みができるようになります。

const action: {type: 'foo'; foo: unknown} | {type: 'bar'; bar: unknown} = {};

if (action.type === 'foo') {
  // action = {type: 'foo'; foo: unknown}
}