Next.js と TypeScript で学ぶ、export interface と type

TwitterFacebookHatena

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.tsIUserインターフェースを定義し、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変数は、idnameプロパティを持つオブジェクトとして定義されています。これがIUserインターフェースの定義に一致しているため、TypeScript の型チェックを通過することができます。

なお、このuser変数をAppコンポーネントで利用して、ユーザーの名前を表示しています。

export type と export interface の違い

実際にはexport typeexport interfaceのどちらを使うかは開発者のスタイルやプロジェクトのルールに依存しますが、いくつか違いはあります。

  • 拡張性: interfaceは拡張(extends)や実装(implements)が可能であるため、より複雑な型関係を表現するのに適しています。一方、typeは主に既存の型を組み合わせて新しい型を定義するのに用いられます。
  • 互換性: TypeScript の古いバージョンではinterfaceの方が多機能でしたが、TypeScript 2.7 以降ではtypeも大抵の機能をサポートしています。
  • 表現力: typeinterfaceよりも表現力が高く、例えば 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 の型を一元管理し、再利用することが可能です。これにより、型安全性が高まり、開発の効率が向上します。

Next.js と TypeScript で学ぶ、export interface と type