普段はTypeScript、React、ReactNativeで実装しているフロントエンドエンジニアです。
今回は実務で使用することの多かった便利なTypeScriptの型をまとめていこうと思います。
Extract
// Extract from T those types that are assignable to U
type Extract<T, U> = T extends U ? T : never;
型 T が型 U に代入可能であれば型 T、そうでなければneverを返す Conditional Type です。
Union型から特定の型を抽出する際に使用されます。
デザイントークンで指定した型から必要なものだけ抽出する際に使用することが多かったです。
type Color = 'red' | 'blue' | 'yellow' | 'black' | 'white';
type ButtonColor = Extract<Color, 'red' | 'yellow'>;
/*
type ButtonColor = 'red' | 'yellow'
*/
Exclude
// Exclude from T those types that are assignable to U
type Exclude<T, U> = T extends U ? never : T;
型 T が型 U に代入可能であればnever、そうでなければ型 T を返す Conditional Type です。
主に Union Types から特定の型を取り除く際に使われます。
Exclude とは逆の役割を果たします。
type Color = 'red' | 'blue' | 'yellow' | 'black' | 'white';
type ButtonColor = Extract<Color, 'red' | 'yellow'>;
// type ButtonColor = 'blue' | 'black' | 'white';
keyof
単体で使用することはあまりありませんでした。
typeof と合わせて使用する場面が多かったです。(オブジェクトのプロパティ名を Union型で取得)
keyof は値ではなく型に使用するので一旦 typeofを挟んでいる形になります。
また他の組み込み関数の定義の内部でもよく使用されています。
type Person = {
name: string;
age: number;
location: string;
};
type K1 = keyof Person;
// "name" | "age" | "location"
const colors = {
white: '#FFFFFF',
mineShaft: '#333333',
chicago: '#555555',
apple: '#5EB520',
export type Color = keyof typeof colors;
// 'white' | 'mineShaft' | 'chicago' | 'apple';
ComponentProps
公式でReactから提供されている型です。
node_modules/@types/react/index.d.ts
ファイルで定義されています。
使用する場面はあまり多くないですが、結構便利だったので紹介します。
ComponentPropsを使用すれば、対象コンポーネントのPropsの型を参照できます。
子コンポーネントで定義したPropsの型やその一部を、親コンポーネントでも使用する場合に使用することが多いです。
対象コンポーネントのPropsの型が変わった場合、変更に応じて自動でComponentPropsを呼び出している側の型定義も変更されるので、余分な修正や修正漏れがなくなるので便利です。
今回は簡単に使用例を紹介するため1ファイルにまとめています。
import React, { ComponentProps } from 'react';
import { Text, View } from 'react-native';
type Props = {
name:string;
age:number;
};
const Sample: React.FC<Props> = ({ name, age }) => {
return (
<View>
<Text>{name}</Text>
<Text>{age}</Text>
</View>
);
};
type SampleProps = React.ComponentProps<typeof Sample>;
/*
type SampleProps = {
name: string;
age: number;
}
*/
type SampleNameProps = {
name: SampleProps['name'];
};
/*
1部分だけ取得
type SampleNameProps = {
name: string;
}
*/
type SampleAgeProps = Pick<SampleProps,'age'>
/*
PickやOmitなどの組み込み関数を使用するとより柔軟に型定義可能
type SampleNameProps = {
age: number;
}
*/
Mapped Types
{[P in K]: T}
ここで、P は識別子、K は文字列に代入可能でなければならない型です。
そして T は K に対する値の型になります。
Mapped Typesを使用することでスマートに型定義が可能になります。
type Item = { a: string, b: number, c: boolean };
type T1 = { [P in "x" | "y"]: number }; // { x: number, y: number }
type T2 = { [P in keyof Item]: Date }; // { a: Date, b: Date, c: Date }
type T3 = { [P in keyof Item]: Item[P] }; // { a: string, b: number, c: boolean }
type Pattern = 'first' | 'second' | 'third';
const patternContents: {
[key in Pattern]: {
title: string;
body: string;
};
} = {
first: {
title: 'サンプル1',
body: '内容1',
},
second: {
title: 'サンプル2',
body: '内容2',
},
third: {
title: 'サンプル3',
body: '内容3',
},
};
まとめ
慣れてくると型定義はパズルみたいで楽しいですね!
記事を執筆するに当たり、改めてドキュメントを読み返すことで新たな発見もありました。
個人的にPick や Omit は結構使っているイメージだったのですが、振り返るとあまり使用していなかったです。
次の機会には自身が詰まった少し応用的な型に関して執筆できたらと思います。