TL;DR
このページでは、TypeScript の interface の実装方法について解説しますね。一言でいうと interface とは、型を定義するための仕組みです。
開発環境 | バージョン |
---|---|
Next.js | 13.4.4 |
TypeScript | 5.0.4 |
Emotion | 11.11.0 |
React | 18.2.0 |
TypeScript の interface とは?
TypeScript の interface は、型の一種です。プロパティやメソッドの名前と型を組み合わせて新しい型を定義するための仕組みです。これにより、オブジェクトの形状を規定し、エディターやコンパイラーがエラーを検出するための情報を提供します。
では、以下に基本的な interface の使い方について見ていきましょう。
interface Person {
name: string
age: number
}
const p: Person = {
name: 'John Doe',
age: 30,
}
このコードでは、Person
という interface を定義しています。この interface はname
とage
というプロパティを持つオブジェクトの型を規定します。このPerson
型の変数p
を定義し、その値としてname
が"John Doe"、age
が 30 のオブジェクトを指定しています。
Optional プロパティを持つ Interface
TypeScript では Interface 内のプロパティを任意にすることができます。プロパティ名の後ろに?
を付けることで、そのプロパティは存在してもしなくても良いことを示します。これは主にオプションのプロパティを持つオブジェクトに有用です。
interface User {
id: string
name: string
age?: number // Optional property
}
Read-Only プロパティを持つ Interface
TypeScript の Interface は、プロパティを読み取り専用にすることもできます。これはプロパティの値が一度設定された後に変更されることを防ぐためです。読み取り専用のプロパティを定義するためには、プロパティ名の前にreadonly
修飾子を付けます。
interface Config {
readonly id: string
name: string
}
関数型を持つ Interface
TypeScript の Interface は、関数の型も定義することができます。これは主に特定の形状の関数を期待する関数やメソッドの引数に対して使用されます。
interface Greeter {
(name: string, age: number): string
}
const greet: Greeter = (name, age) => {
return `Hello, ${name}. You are ${age} years old.`
}
Indexable Types を持つ Interface
TypeScript の Interface は、インデックス可能な型も定義することができます。これは主に、一部または全部が動的に設定されるオブジェクトに対して使用されます。
interface StringArray {
[index: number]: string
}
const myArray: StringArray = ['Bob', 'Fred']
クラスと Interface
TypeScript の Interface は、クラスが特定の契約を満たすことを強制するためにも使用できます。Interface を実装するには、クラス定義の後にimplements
キーワードと Interface の名前を追加します。
interface ClockInterface {
currentTime: Date
setTime(d: Date): void
}
class Clock implements ClockInterface {
currentTime: Date = new Date()
setTime(d: Date) {
this.currentTime = d
}
}
ハイブリッド型を持つ Interface
Interface は混合型(またはハイブリッド型)を記述することもできます。これは、オブジェクトが関数であると同時にプロパティを持つことができる JavaScript の特性を反映したものです。
interface Counter {
(start: number): string
interval: number
reset(): void
}
function getCounter(): Counter {
let counter = function (start: number) {} as Counter
counter.interval = 123
counter.reset = function () {}
return counter
}
let c = getCounter()
c(10)
c.reset()
c.interval = 5.0
Interface の拡張
Interface は他の Interface を拡張することができます。これにより、複数の Interface が同じ基本プロパティを共有している場合に、コードの重複を避けることができます。
interface Shape {
color: string
}
interface Square extends Shape {
sideLength: number
}
let square = {} as Square
square.color = 'blue'
square.sideLength = 10
Interface と型別名の相互適用
Interface と型別名(type aliases)は組み合わせて使用することができます。これにより、より複雑な型の記述が可能となります。
type Point = {
x: number
y: number
}
interface Circle {
center: Point
radius: number
}
let myCircle: Circle = {
center: { x: 0, y: 0 },
radius: 5,
}
Interface の合成
Interface は複数の Interface を 1 つに合成することも可能です。この機能はクラスが複数の Interface を実装できるようにすることで特に有用です。
interface Animal {
breathe(): void
}
interface Mammal {
feedMilk(): void
}
interface Human extends Animal, Mammal {
speak(): void
}
class Person implements Human {
breathe() {
console.log('Breathing...')
}
feedMilk() {
console.log('Feeding milk...')
}
speak() {
console.log('Speaking...')
}
}
インターフェイスを使用したオブジェクトの配列
TypeScript では、interface
を使用してカスタム型を定義することができます。これは、オブジェクトが特定の形状(つまり、特定のプロパティとその型)を持つことを保証するための強力な方法です。これを配列と組み合わせて使用すると、オブジェクトの配列を効果的に型付けすることができます。
以下の例では、User
というインターフェイスを定義し、その配列をusers
という名前の変数で宣言しています。
interface User {
id: number
name: string
}
const users: User[] = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
// more users...
]
インターフェイスで配列を含むオブジェクトの定義
インターフェイスを使用して、配列をプロパティとして持つオブジェクトを定義することも可能です。以下の例では、Company
というインターフェイスにemployees
というプロパティを持たせ、その型をUser[]
と定義しています。
interface User {
id: number
name: string
}
interface Company {
name: string
employees: User[]
}
const company: Company = {
name: 'OpenAI',
employees: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
// more employees...
],
}
Next.js で、TypeScript の interface を実装
では、次に Next.js のプロジェクトで TypeScript の interface をどのように使うかについて見ていきましょう。
まずは、Next.js のページコンポーネントを作成します。以下のコードはpages/index.tsx
というファイルで定義します。
import type { NextPage } from 'next'
import { UserProfile } from '../components/UserProfile'
type User = {
name: string
age: number
}
const HomePage: NextPage = () => {
const user: User = {
name: 'John Doe',
age: 30,
}
return <UserProfile user={user} />
}
export default HomePage
このコードでは、まずUser
という interface を定義しています。これは先程と同じように、ユーザーを表すオブジェクトの型を規定します。次にHomePage
というページコンポーネントを定義し、その中でuser
というUser
型の変数を定義し、値としてユーザーの情報を持つオブジェクトを指定しています。そして、このuser
をUserProfile
というコンポーネントに渡しています。
コンポーネントのプロパティの型定義
先程作成したUserProfile
コンポーネントの中身を見てみましょう。以下のコードはcomponents/UserProfile.tsx
というファイルで定義します。
type User = {
name: string
age: number
}
type Props = {
user: User
}
const UserProfile = ({ user }: Props) => {
return (
<div>
<p>Name: {user.name}</p>
<p>Age: {user.age}</p>
</div>
)
}
export { UserProfile }
ここでもまたUser
という interface を定義していますが、このコンポーネントが受け取るプロパティの型として、更にProps
という interface を定義しています。このProps
はuser
というプロパティを持つことを規定し、その型は先程定義したUser
となります。そして、このProps
型を引数の型として指定し、UserProfile
コンポーネントを定義しています。このコンポーネントでは、受け取ったuser
の情報を表示します。
コンポーネント間のデータの型定義
次に、異なるコンポーネント間でデータを渡す際の型定義の例を見てみましょう。以下のコードはcomponents/UserList.tsx
というファイルで定義します。
import { UserProfile } from './UserProfile'
type User = {
id: number
name: string
age: number
}
type Props = {
users: User[]
}
const UserList = ({ users }: Props) => {
return (
<div>
{users.map((user) => (
<UserProfile key={user.id} user={user} />
))}
</div>
)
}
export { UserList }
このコードでは、複数のユーザー情報を持つ配列を受け取り、その各ユーザーについてUserProfile
コンポーネントを表示します。これにより、複数のユーザー情報を一覧として表示することができます。
users: User[] はどういう意味?
User[]
は User 型の配列を表します。つまり、users
は User 型のオブジェクトが複数含まれる配列を指しています。
まず、最初のtype User
の部分では、User
という新たな型を定義しています。このUser
型は、id
(数値)、name
(文字列)、age
(数値)というプロパティを持つオブジェクトです。
次に、type Props
の部分では、Props
という新たな型を定義しています。このProps
型は、User[]
型のusers
というプロパティを持つオブジェクトです。User[]
は「User 型の配列」という意味になります。つまり、users
プロパティは User 型のオブジェクトが複数含まれる配列を表すことになります。
以下のようなデータ構造を想像するとわかりやすいかもしれません。
let someProps: Props = {
users: [
{ id: 1, name: 'Alice', age: 20 },
{ id: 2, name: 'Bob', age: 25 },
{ id: 3, name: 'Charlie', age: 30 },
// more users...
],
}
上記の例では、someProps
はProps
型であり、そのusers
プロパティは 3 つのUser
型のオブジェクトを含む配列です。