TL;DR
このページでは、TypeScript の機能の一つである「export type」について具体的な実装方法を解説しますね。まずは基本からしっかりと理解し、その上で実際の Next.js プロジェクトに応用していきましょう。
開発環境 | バージョン |
---|---|
Next.js | 13.4.4 |
TypeScript | 5.0.4 |
Emotion | 11.11.0 |
React | 18.2.0 |
export type
TypeScript には、型情報を他のファイルに共有するための仕組みとして「export」が存在します。これを用いて、定義した型情報を外部のファイルから参照できます。そして、「export」には大きく分けて二つの種類があります。一つは「export」で、もう一つが「export type」です。
「export」は、変数、関数、クラス、型など様々な要素をエクスポートできます。一方、「export type」は型だけをエクスポートできる機能です。この「export type」を使用することで、コードの読み手に対して、「これは型情報だけをエクスポートしていますよ」と明示的に伝えることができます。
// types.ts
export type User = {
id: string
name: string
}
export interface
export interface
は、TypeScript においてインターフェースを外部ファイルに共有するための方法です。これを使うことで他のファイルからそのインターフェースをインポートして利用することができます。インターフェースは、オブジェクトが持つべき構造を定義するのに用いられます。
以下に、export interface
を使った例を示します。
// types.ts
export interface IUser {
id: string
name: string
}
// pages/index.tsx
import { IUser } from '../types'
const HomePage = ({ user }: { user: IUser }) => {
return (
<div>
<p>{user.name}</p>
</div>
)
}
export default HomePage
この例では、types.ts
でIUser
インターフェースを定義し、export interface
を用いてそれを外部に公開しています。そして、pages/index.tsx
ではそのIUser
インターフェースをインポートし、ユーザー情報を扱っています。
それでは、export interface
を使ってインターフェースをエクスポートし、それを別のファイルからインポートする例を見てみましょう。
まず、IUser
というインターフェースをtypes.ts
というファイルで定義し、それをエクスポートします。
// types.ts
export interface IUser {
id: string
name: string
}
次に、このIUser
インターフェースをApp.tsx
というファイルでインポートします。
// App.tsx
import { IUser } from './types'
const user: IUser = {
id: '1',
name: 'John Doe',
}
const App = () => {
return (
<div>
<p>{`User Name: ${user.name}`}</p>
</div>
)
}
export default App
ここで、IUser
インターフェースを使ってuser
変数を定義しています。このuser
変数は、id
とname
プロパティを持つオブジェクトとして定義されています。これがIUser
インターフェースの定義に一致しているため、TypeScript の型チェックを通過することができます。
なお、このuser
変数をApp
コンポーネントで利用して、ユーザーの名前を表示しています。
export type と export interface の違い
実際にはexport type
とexport interface
のどちらを使うかは開発者のスタイルやプロジェクトのルールに依存しますが、いくつか違いはあります。
- 拡張性:
interface
は拡張(extends)や実装(implements)が可能であるため、より複雑な型関係を表現するのに適しています。一方、type
は主に既存の型を組み合わせて新しい型を定義するのに用いられます。 - 互換性: TypeScript の古いバージョンでは
interface
の方が多機能でしたが、TypeScript 2.7 以降ではtype
も大抵の機能をサポートしています。 - 表現力:
type
はinterface
よりも表現力が高く、例えば Union 型や Intersection 型、タプル、リテラル型などを定義することができます。
そのため、実際の開発では、どちらを使うべきかは要件や利便性によるところが大きいです。また、export type
があまり見かけないのは、TypeScript の早い段階ではinterface
がより高機能であったため、既存のコードベースやスタイルガイドがinterface
を推奨している場合が多いからかもしれません。
Next.js で、"export type" を実装
具体的な実装例を見てみましょう。以下に示すコードは Next.js でのページコンポーネントの一例です。User 型をエクスポートし、その型を利用してユーザーデータを扱います。
// types.ts
export type User = {
id: string
name: string
}
// pages/index.tsx
import { User } from '../types'
const HomePage = ({ user }: { user: User }) => {
return (
<div>
<p>{user.name}</p>
</div>
)
}
export default HomePage
このコードでは、types.ts
から User 型をインポートし、HomePage コンポーネントの props として使用しています。このように、TypeScript では型情報を別ファイルに分けて管理し、必要な場所でインポートして使用することができます。
export type を活用
さらに、「export type」を活用すると、より複雑な型の管理も可能になります。例えば、アプリケーションで使用するすべての型を一つのファイルで管理し、「export type」を用いて型情報を共有するという方法があります。
// types.ts
export type User = {
id: string
name: string
}
export type Post = {
id: string
title: string
author: User
}
// pages/index.tsx
import { User, Post } from '../types'
const HomePage = ({ user, post }: { user: User; post: Post }) => {
return (
<div>
<p>{user.name}</p>
<p>{post.title}</p>
</div>
)
}
export default HomePage
Emotion で実装
Emotion を使って、「export type」を活用する場面を見てみましょう。Emotion を使用すると、props に基づいてスタイリングを動的に変更することが可能です。
// styles.ts
import styled from '@emotion/styled'
export const UserCard = styled.div<{ isActive: boolean }>`
padding: 20px;
background: ${(props) => (props.isActive ? 'green' : 'red')};
`
// types.ts
export type User = {
id: string
name: string
isActive: boolean
}
// pages/index.tsx
import { UserCard } from '../styles'
import { User } from '../types'
const HomePage = ({ user }: { user: User }) => {
return (
<UserCard isActive={user.isActive}>
<p>{user.name}</p>
</UserCard>
)
}
export default HomePage
このコードでは、UserCard コンポーネントが受け取る props の型を明示的に定義しています。このように、「export type」を使うことで、コンポーネントが受け取る props の型を一元管理し、再利用することが可能です。これにより、型安全性が高まり、開発の効率が向上します。