🙆♀️
[TypeScript]色々な型表現
はじめに
TypeScriptには様々なデータ型の活用法があるのでメモとして残していく
TypeScriptの4.9.5
で確認
型ガード
条件によって特定の型を絞り込むことができる
プリミティブ型でよく使われる型ガード
const typeGuard = (x: string | number) => {
if (typeof x === 'string') {
// xはstring型だと推論できる
} else if (typeof x === 'number') {
// xはnumber型だと推論できる
}
}
オブジェクト型でよく使われる型ガード
in
演算子を使用した型ガード。大きく2種類。
1 構造的部分型による意図しない挙動をするパターン
下記コードの①のhoge
とfuga
プロパティを持ったx
が構造的部分型により代入できてしまう。つまり、「Fuga型の時はfuga
プロパティしか持っていない」、「Hoge型の時はhoge
プロパティしか持っていない」というわけではない。そのようなケースではif文で理想の分岐ができないケースもあるので注意
type Fuga = {
fuga: string;
}
type Hoge = {
hoge: string;
}
const typeGuard = (x: Fuga | Hoge) => {
if ('hoge' in x) {
// xがHoge型の時のみ通ってほしいが、そうならないパターンもある
} else if ('fuga' in x) {
// xがFuga型の時のみ通ってほしいが、そうならないパターンもある
}
}
const x = {
hoge: 'hoge',
fuga: 'fuga',
}
// ①
const fuga: Fuga | Hoge = x;
typeGuard(fuga)
2 構造的部分型による意図しない挙動を避ける型ガード(タグ付きUnion型を使ったオブジェクト型の型ガード)
Union型を構成する型に共通プロパティを追加。それを利用してif分岐を行う
type Hoge = {
type: 'hoge';
hoge: string;
}
type Fuga = {
type: 'fuga';
fuga: string;
}
// `type`により意図した条件分岐ができる
const typeGuard = (x: Fuga | Hoge) => {
if (x.type === 'fuga') {
// Fuga型
} else if (x.type === 'hoge') {
// Hoge型
}
}
網羅性チェック
never
型を用いて網羅性チェックができる
never
型にはどんな値もセットできないことを利用した方法
type Fuga = {
type: 'fuga';
fuga: string;
}
type Hoge = {
type: 'hoge';
piyo: string;
}
type HogeFuga = Hoge | Fuga;
const exhaustiveCheck = (x: HogeFuga) => {
if (x.type === 'fuga') {
// 何か処理
} else if (x.type === 'hoge') {
// 何か処理
} else {
const _exhaustiveCheck: never = x;
}
}
新しい型を追加するとエラーとなり、考慮漏れに気付ける
type Piyo = {
type: 'piyo';
piyo: string;
}
// Piyoを追加
type HogeFugaPiyo = Hoge | Fuga | Piyo;
const exhaustiveCheck = (x: HogeFugaPiyo) => {
if (x.type === 'fuga') {
// 何か処理する
} else if (x.type === 'hoge') {
// 何か処理する
} else {
// Type 'Piyo' is not assignable to type 'never'. というエラーとなる
const _exhaustiveCheck: never = x;
}
}
インデックスシグネチャ
インデックスでオブジェクトのプロパティとその型を定義する機能
type indexSignature = {
[key: string]: string;
};
デメリット
存在しないプロパティに対する推論に危険性がある
下記コードの場合string型で推論されてしまう
const x: indexSignature = {}
// `x.id`は実際には存在しないが、indexSignature[string]: string のようにstring型で推論されてしまう
console.log('x', x.id)
上記デメリットの対策
tsconfig.json
のcompilerOptions
にnoUncheckedIndexedAccess
を追加してtrue
を設定する
すると、undefined
が推論される
const x: indexSignature = {}
// indexSignature[string]: string | undefined undefinedが推論されるようになる
console.log('x', x.id)
never
型を組み合わせたUnion型のフィルタリング
Conditional Typesとtype Fuga = {
type: 'fuga';
fuga: string;
}
type Hoge = {
type: 'hoge';
hoge: string;
}
type Piyo = {
type: 'piyo';
piyo: string;
}
type HogeFuga = Hoge | Fuga;
type Filter<T> = T extends HogeFuga ? T : never;
// Hoge | Fuga となる
type HogeFugaFilter = Filter<Hoge | Fuga | Piyo>
?
修飾子が付与されたプロパティとundefined
のUnion型のUnion型の違い
例えば下記2つの違い
type Hoge = {
hoge?: string;
}
type Hoge = {
hoge: string | undefined;
}
?
修飾子の場合はプロパティを明示的に宣言しなくても問題ない
type Hoge = {
hoge?: string;
}
const x: Hoge = {}
undefined
のUnion型の場合hogeプロパティを省略できない
type Hoge = {
hoge: string | undefined;
}
// Property 'hoge' is missing in type '{}' but required in type 'Hoge'
const x: Hoge = {}
オブジェクトのプロパティをUnion型で表現
const hogefuga = {
'hoge': 'hogehoge’
'fuga': 'fugafuga’
}
// let obj: "hoge" | "fuga"
let obj: keyof typeof hogefuga;
オブジェクトのプロパティのデータ型を取得
あるオブジェクトのプロパティの型をUnion型で表現
const hogefuga = {
'hoge': 'hogehoge',
'fuga': 'fugafuga'
} as const;
// type Hoge = "hogehoge" | "fugafuga"
type Hoge = typeof hoge[keyof typeof hogefuga]
配列の値を型として設定
const arr = [
{
hoge: 'hoge'
},
];
type Hoge = typeof arr[number]
// 上記は以下のように型定義される
// type Hoge = {
// hoge: string;
// }
const colors = ['red', 'blue', 'yellow'] as const;
type Color = typeof colors[number];
// 上記は下記のように配列の中身をUnion型で取得して型定義される
// type Color = "red" | "blue" | "yellow"
Union型でenumのような表現
const Color = {
RED: 'red',
BLUE: 'blue',
YELLOW: 'yellow'
} as const;
type ColorType = typeof Color[keyof typeof Color]
const color: ColorType = Color.BLUE;
おわりに
データ型の表現・使い方は色々あるので使いながら慣れていく
Discussion