目次
TypeScriptには、型定義や型チェックの方法がたくさんありますが、全てを覚えることは難しいです。
本記事では、その中でも実務ベースで知っておくと役立つ内容に絞ってご紹介します。
ReactやNext.jsでTypeScriptを使用する場合についても記載します。
Utility Types
TypeScriptが提供している型変換を助けてくれるユーティリティ型です。
グローバルにどこからでも使用することができます。
https://www.typescriptlang.org/docs/handbook/utility-types.html
Utility Types
は、一通り目を通すことをおすすめしますが、
その中でも特に使用率が高いものを独断と偏見でご紹介します。
Pick<T, K>
オブジェクト型から特定のプロパティを抜き出して型を作りたい場合に使用します。
オブジェクト型T
からK
(文字列または文字列のユニオン型)で指定したプロパティのみを含む型を返します。
type User = {
firstName: string;
lastName: string;
createdAt: string;
updatedAt: string;
};
type Person = Pick<User, "firstName" | "lastName">;
// Person
// firstName: string;
// lastName: string;
Omit<T, K>
オブジェクト型から特定のプロパティを除いて型を作りたい場合に使用します。
オブジェクト型T
からK
(文字列または文字列のユニオン型)で指定したプロパティを除いた型を返します。
type User = {
firstName: string;
lastName: string;
createdAt: string;
updatedAt: string;
};
type Person = Omit<User, "createdAt" | "updatedAt">;
// Person
// firstName: string;
// lastName: string;
ReturnType<T>
関数の戻り値から型を作りたい場合に使用します。T
には関数の型を指定します。
type T0 = ReturnType<() => string>;
// T0 = string;
const f = () => ({ a: 0, b: 'b' });
type T1 = ReturnType<typeof f>; // typeof で型に変換して指定する
// T1 = { a: number, b: string };
型チェック
プリミティブ型の判定
プリミティブ型の判定にはtypeof
を利用します。typeof
を使うと変数の型を取得できるので、そちらを元に判定します。
const foo = getFoo();
// foo = string | number
if (typeof foo === 'string') {
// foo = string
}
オブジェクト型の判定
オブジェクト型の判定にはinstanceof
を利用します。
const foo = getFoo();
// foo = string | string[]
if (foo instanceof Array) {
// foo = string[]
}
オブジェクトが特定のプロパティを持つか判定
特定のプロパティを持っているかの判定にはin
を利用します。
const foo = getFoo();
// foo = string | { error: ... }
if ('error' in foo) {
// foo = { error: ... }
}
nullとundefinedの判定
== null
or != null
で判定することで、null
とundefined
の両方をチェックすることができます。
(==
自体はjavascriptの機能ですが、TypeScriptできちんと型推論されるので合わせて記載しています。)
const f = (a?: number | null) => {
if (a == null) return;
// a = number
};
型の構築
オブジェクトから型を作る
オブジェクトから型を作ることで、enum
の代わりに使うことができます。enum
には問題点があるので、こちらの方法を活用すると良いです。enum
の問題点:https://www.kabuku.co.jp/developers/good-bye-typescript-enum
// Bad
enum Mode {
Eazy = 'eazy',
Normal = 'normal',
Hard = 'hard',
}
// Good
const Mode = {
Eazy: 'eazy',
Normal: 'normal',
Hard: 'hard',
} as const;
type Mode = typeof Mode[keyof typeof Mode];
// MODE = 'eazy' | 'normal' | 'hard';
// 反復処理も可能
Object.values(Mode).forEach(mode => { ... });
enum
の代わりに単純なユニオン型を使用することもあると思います。
こちらを利用するメリットは、「MODE.EAZY
のように書ける」「反服処理が可能」なので、よりenum
と同じ感覚で使えます。用途に合わせて使い分けるといいと思います。
React関連
コンポーネントから型を作る
React.ComponentProps<T>
を使うと、指定したコンポーネントT
のPropsから型を作ることができます。
コンポーネントから型を作ることで、型を再定義しなくていいので保守性が高まります。
type ChildProps = {
className: string;
value: string;
};
const Child = (props: ChildProps) => (...);
type Props = React.ComponentProps<typeof Child>;
// Props
// className: string;
// value: string;
HTML標準タグ(div, span, aなど)から作ることもできます。
HTML標準タグをラップしたコンポーネントを作る際は、こちらを使うと便利です。
type Props = React.ComponentProps<'button'>; // 文字列リテラルで指定する
const Button = (props: Props) => {
return <button {...props}>ボタン</button>;
};
Refを明示的にする
ComponentProps
に似た定義で、ComponentPropsWithRef
, ComponentPropsWithoutRef
があります。使い方はComponentProps
と同じですが、「refが付いている・付いていない」が明示的に分かるようになっています。
こちらにも書かれていますが、ComponentProps
よりComponentPropsWithRef
, ComponentPropsWithoutRef
の方が明示的で分かりやすいので推奨されています。
Useful Patterns by Use Case | React TypeScript Cheatsheets
Next.js関連
getStaticPropsからPropsの型を定義する
getStaticProps
からProps
が返されるので、そちらを元に型定義する方法です。
nextが用意しているInferGetStaticPropsType
を使います。
import { InferGetStaticPropsType } from 'next';
type Data = { ... };
export const getStaticProps = async () => {
const res = await fetch('https://.../data');
const data: Data = await res.json();
return {
props: {
data,
},
};
};
type Props = InferGetStaticPropsType<typeof getStaticProps>;
const Page = ({ data }: Props) => {
// ...
};
export default Page;
注意点としては、getStaticProps
にGetStaticProps<Props, Query>
のProps
を省略している形で指定していると、デフォルトの{ [key: string]: any; }
で型推論されてしまいます。
import { GetStaticProps, InferGetStaticPropsType } from 'next'
export const getStaticProps: GetStaticProps = async () => {
// ...
return {
props: {
data,
},
}
};
type Props = InferGetStaticPropsType<typeof getStaticProps>;
// Props = { [key: string]: any; }
// `data`が渡されていることを型推論できない
GetStaticProps
にProps
を正しく指定することで型推論させることは可能ですが、InferGetStaticPropsType
に比べて修正範囲が多くなります。なので、InferGetStaticPropsType
を使った方法が良いです。
GetStaticPropsの場合の修正範囲
・Propsの型定義の修正
・getStaticPropsの処理の修正
InferGetStaticPropsTypeの場合の修正範囲
・getStaticPropsの処理の修正
余談ですが、getStaticProps
の引数でcontext
を使う場合、GetStaticProps
の代わりにGetStaticPropsContext<Q>
を使うことができるのでGetStaticProps
は使わなくて問題ありません。
import { GetStaticPropsContext } from 'next';
export const getStaticProps = async (context: GetStaticPropsContext) => {
// ...
};
getServerSidePropsからPropsの型を定義する
getServerSideProps
ではInferGetServerSidePropsType
を使用します。使い方はSSGの時と同じです。
import { InferGetServerSidePropsType } from 'next';
type Data = { ... };
export const getServerSideProps = async () => {
const res = await fetch('https://.../data');
const data: Data = await res.json();
return {
props: {
data,
},
};
};
type Props = InferGetServerSidePropsType<typeof getServerSideProps>;
const Page = ({ data }: Props) => {
// ...
};
export default Page;