オブジェクトな値なら何でも入れることができる型です。
型にはobject
を使います。
const obj: object = {};
構造化したオブジェクトを Interface で定義する
ある程度構造化したobject
が使いたいなら、interface
で型を作ります。以下はfoo
とbar
というプロパティを持つ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
が来た時にfoo
かbar
の値どちらかが取得できるということを表しています。それはfoo
とbar
プロパティがある為で、値の型は取得できるうるすべてを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;
このpartialValue
は、次のように書くのと同じ意味になります。
interface PartialValue {
foo?: number;
bar?: number;
}
Record 型で一気に定義する
すべてのプロパティが同じ型の時便利なのがRecord<P extends string, V>
型です。P
は、Unionなstring
、V
はそれぞれプロパティとなる値を設定します。つまり、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
で継承しているのでfoo
とbar
の記述はいりません。
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}
}