TypeScript ジェネリクス入門:`<T>` の基本と型制約の書き方

TypeScript ジェネリクス入門:<T> の基本と型制約の書き方

ジェネリクス(Generics)は、型を引数として受け取れる仕組みです。同じ処理を異なる型に対して型安全に使い回せます。


なぜジェネリクスが必要か

any を使うと型の恩恵がなくなります。

// any を使う場合:型チェックが効かない
function identity(arg: any): any {
  return arg
}

const result = identity(42)
result.toUpperCase() // 実行時エラーだが TypeScript は気づけない

ジェネリクスを使うと、型を保ったまま汎用的に書けます。

// ジェネリクスを使う場合:型が保たれる
function identity<T>(arg: T): T {
  return arg
}

const num = identity(42)       // num: number
const str = identity("hello")  // str: string

基本の書き方

関数名の後に <T> を付けて型引数を宣言します。T は慣習的な名前で、任意の名前を使えます。

function wrap<T>(value: T): { value: T } {
  return { value }
}

const wrapped = wrap("hello") // { value: string }

型引数は明示的に指定することもできます。

const result = identity<number>(42)

配列を受け取る例

function first<T>(arr: T[]): T | undefined {
  return arr[0]
}

const num = first([1, 2, 3])      // number | undefined
const str = first(["a", "b"])     // string | undefined

interface と type でのジェネリクス

// interface
interface Box<T> {
  value: T
  label: string
}

const numBox: Box<number> = { value: 42, label: "数値" }
const strBox: Box<string> = { value: "hello", label: "文字列" }

// type alias
type Pair<T, U> = {
  first: T
  second: U
}

const pair: Pair<string, number> = { first: "age", second: 30 }

型制約(extends)

型引数に制約を付けることで、使えるメソッドや型を絞れます。

// length プロパティを持つ型だけ受け付ける
function getLength<T extends { length: number }>(arg: T): number {
  return arg.length
}

getLength("hello")   // 5
getLength([1, 2, 3]) // 3
getLength(42)        // エラー:number に length はない

特定の型のサブタイプに絞ることもできます。

type Animal = { name: string }

function getName<T extends Animal>(animal: T): string {
  return animal.name
}

keyof との組み合わせ

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

const user = { name: "Alice", age: 30 }

const name = getProperty(user, "name") // string
const age  = getProperty(user, "age")  // number
getProperty(user, "email")             // エラー:存在しないキー

よく使うパターンまとめ

パターン 書き方 用途
基本 <T> 型を引数として受け取る
複数の型引数 <T, U> 2種類以上の型を扱う
型制約 <T extends 型> 使える型を絞る
keyof 制約 <K extends keyof T> オブジェクトのキーを安全に扱う
デフォルト型 <T = string> 省略時のデフォルト型を指定

TypeScript の型システム全般については「TypeScript バージョン確認:tsc version コマンドまとめ」も参照してください。

DNS nameserver の種類と設定:1.1.1.1・8.8.8.8 などパブリックDNS一覧

DNS nameserver の種類と設定:1.1.1.1・8.8.8.8 などパブリックDNS一覧

DNS(Domain Name System)はドメイン名を IP アドレスに変換する仕組みです。どの DNS サーバーを使うかは /etc/resolv.conf で確認・設定できます。


/etc/resolv.conf の見方

cat /etc/resolv.conf
nameserver 1.1.1.1
nameserver 8.8.8.8
search local
設定 説明
nameserver 使用する DNS サーバーの IP アドレス。複数行書けば上から順に試される
search ホスト名だけで検索したときに補完されるドメイン

nameserver に現れる特殊なアドレス

アドレス 意味
0.0.0.0 未設定・無効な状態。名前解決できない
127.0.0.53 systemd-resolved(Linux)がローカルで受け取るアドレス
127.0.0.1 ローカルホスト。Pi-hole など自前の DNS を動かしている場合
192.168.x.x ルーターの DNS(ホームネットワークでよく見られる)

nameserver 0.0.0.0 が設定されている場合は DNS が正しく設定されていない状態です。Colima や Docker の環境でネットワーク設定が崩れたときに現れることがあります。


主要パブリック DNS 一覧

提供元 プライマリ セカンダリ 特徴
Cloudflare 1.1.1.1 1.0.0.1 高速・プライバシー重視。応答速度が世界最速クラス
Google 8.8.8.8 8.8.4.4 安定性が高く最も広く使われている
Quad9 9.9.9.9 149.112.112.112 悪意あるドメインをブロックするセキュリティ機能あり
OpenDNS 208.67.222.222 208.67.220.220 フィルタリング機能あり。Cisco 傘下

開発環境では 1.1.1.1(Cloudflare)か 8.8.8.8(Google)を指定することが多いです。


dig で特定の DNS サーバーを使って確認する

@ でクエリ先の DNS サーバーを指定できます。

# Cloudflare の DNS で example.com を引く
dig @1.1.1.1 example.com

# Google の DNS で引く
dig @8.8.8.8 example.com

# 現在の nameserver 設定で引く(デフォルト)
dig example.com

DNS を変更したあと、意図した DNS サーバーで正しく解決できるかを確認するときに使います。


macOS で DNS を一時的に変更する

# /etc/resolv.conf を直接編集(再起動で元に戻る場合がある)
sudo sh -c 'echo "nameserver 1.1.1.1" > /etc/resolv.conf'

macOS では「システム設定 → ネットワーク → DNS」から変更するのが確実です。


まとめ

目的 使う DNS
速度重視 1.1.1.1(Cloudflare)
安定性重視 8.8.8.8(Google)
セキュリティ重視 9.9.9.9(Quad9)
DNS 未設定の状態 0.0.0.0(名前解決不可)

DNS の動作確認コマンドについては「dig・nslookup入門:DNS名前解決とレコード確認コマンド」も参照してください。

lsof コマンドの使い方:使用中のポートとプロセスを調べる

lsof コマンドの使い方:使用中のポートとプロセスを調べる

lsof(List Open Files)は、プロセスが開いているファイルやネットワーク接続を一覧表示するコマンドです。「このポートを使っているプロセスはどれか」を調べるときに特に役立ちます。

macOS・Linux どちらでも標準で使えます。


ポートを使用しているプロセスを調べる

開発中に「Address already in use」エラーが出たときに使います。

# ポート 3000 を使っているプロセスを調べる
lsof -i :3000
COMMAND   PID   USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
node     1234   user   23u  IPv6  0x...      0t0  TCP *:3000 (LISTEN)

PID 列の番号がプロセス ID です。そのまま kill に渡せます。

# プロセスを終了する
kill -9 1234

複数ポートをまとめて確認することもできます。

lsof -i :3000 -i :8080

TCP / UDP の一覧を表示する

# TCP 接続の一覧
lsof -i tcp

# UDP の一覧
lsof -i udp

# LISTEN 中のポートだけ表示
lsof -i tcp -s TCP:LISTEN

特定プロセスが開いているファイルを調べる

# PID 1234 のプロセスが開いているファイル一覧
lsof -p 1234

特定ファイルを開いているプロセスを調べる

ファイルが「使用中で削除できない」ときに、どのプロセスが掴んでいるか確認できます。

lsof /var/log/app.log
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
tail    5678 user    3r   REG  ...         0 1234 /var/log/app.log

ユーザーが開いているファイルを調べる

# 特定ユーザーのファイル一覧
lsof -u username

よく使うオプションまとめ

コマンド 説明
lsof -i :3000 ポート 3000 を使用中のプロセス
lsof -i tcp -s TCP:LISTEN LISTEN 中の TCP ポート一覧
lsof -i tcp TCP 接続の一覧
lsof -i udp UDP の一覧
lsof -p 1234 PID 1234 が開いているファイル
lsof /path/to/file 指定ファイルを開いているプロセス
lsof -u username ユーザーが開いているファイル

プロセス管理には ps コマンドも組み合わせて使うと便利です。lsof で PID を特定し、ps aux | grep 1234 で詳細を確認するのがよくある流れです。

.ssh の各ファイルの役割:id_ed25519・id_rsa・authorized_keys

.ssh の各ファイルの役割:id_ed25519・id_rsa・authorized_keys

SSH を使っていると ~/.ssh/ ディレクトリにさまざまなファイルが作られます。それぞれのファイルが何のためにあるのかをまとめます。


ファイル一覧と役割

ファイル 役割
id_ed25519 Ed25519で生成した秘密鍵。外部に漏らしてはいけない
id_ed25519.pub 対応する公開鍵。GitHubや接続先サーバーに登録する
id_rsa RSAで生成した秘密鍵(旧来のアルゴリズム)
id_rsa.pub RSA公開鍵
authorized_keys サーバー側に置く。接続を許可するクライアントの公開鍵リスト
known_hosts 接続済みホストの公開鍵を記録するファイル。自動生成される
config ホストごとのSSH設定(ユーザー名・鍵ファイルパスなど)

秘密鍵と公開鍵のペア

id_ed25519id_ed25519.pub はセットで生成されます。

# Ed25519 で鍵ペアを生成
ssh-keygen -t ed25519 -C "your@email.com"
  • 秘密鍵id_ed25519):手元のマシンに保管する。絶対に外部に出さない
  • 公開鍵id_ed25519.pub):接続先に渡す鍵。漏れても問題ない

公開鍵の中身を確認するには:

cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... your@email.com

この出力をそのまま GitHub の「SSH keys」や接続先サーバーの ~/.ssh/authorized_keys に貼り付けます。


id_ed25519 と id_rsa の違い

id_ed25519 id_rsa
アルゴリズム Ed25519(楕円曲線) RSA
鍵長 256bit固定 2048〜4096bit
速度 速い 遅い
セキュリティ 高い 十分だが Ed25519 推奨
対応状況 比較的新しい(2014年〜) 古くから対応済み

特別な理由がなければ id_ed25519 を使うのが現在の標準です。


authorized_keys(サーバー側のファイル)

authorized_keys はクライアント(手元のPC)ではなく、接続先のサーバーに置くファイルです。

# サーバーの ~/.ssh/authorized_keys に公開鍵を追加
cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys

または ssh-copy-id コマンドでまとめて送れます:

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server

1行1つの公開鍵を記載でき、複数のクライアントからの接続を許可できます。


known_hosts(自動生成)

known_hosts は初めてSSHで接続したときに自動で書き込まれます。次回以降の接続で「このホストが本物か」を確認するために使われます。

# 内容確認
cat ~/.ssh/known_hosts

サーバーを再構築した際などに「警告: リモートホストの認証が変わった」というエラーが出たら、古いエントリを削除します:

ssh-keygen -R hostname

config(接続設定)

~/.ssh/config を作ると、接続先ごとの設定をまとめられます。

Host github
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519

Host myserver
  HostName 192.168.1.10
  User ubuntu
  IdentityFile ~/.ssh/id_ed25519
  Port 22

設定後は ssh githubssh myserver と短く書けます。


まとめ

ファイル 置く場所 用途
id_ed25519 手元のPC 秘密鍵(外部流出NG)
id_ed25519.pub 手元のPC 公開鍵(GitHubなどに登録)
authorized_keys 接続先サーバー 接続を許可する公開鍵リスト
known_hosts 手元のPC 接続済みホストの記録(自動生成)
config 手元のPC ホストごとのSSH接続設定

SSH鍵の生成方法は「ssh-keygen 入門:SSHキーペアの生成・確認・GitHubへの登録」も参照してください。

環境変数の export と PATH:.zshrc での書く順番に注意

環境変数の export と PATH:.zshrc での書く順番に注意

.zshrc.bashrc でツールのパスを設定するとき、書く順番によって export が正常に機能しないことがあります。


問題のある設定例

# NG: この時点で mise は PATH に入っていないので失敗する
export MISE_ROOT=$(mise root)        # command not found: mise

# mise を PATH に追加するのはここ(遅い)
export PATH="$HOME/.local/bin:$PATH"

$(mise root) を評価しようとした時点では mise はまだ PATH に存在しません。結果として MISE_ROOT は空のまま、またはエラーになります。


なぜこうなるのか

.zshrc はシェル起動時に上から順に実行されます。コマンド置換 $(...) はその行が評価されるタイミングで即座に実行されるため、まだ PATH に追加されていないコマンドは command not found になります。

シェル起動
  ↓
1行目: export HOGE=$(func)           ← この時点の PATH でコマンドを探す
  ↓                                     → func が見つからず HOGE は空になる
2行目: export PATH="$HOME/bin:$PATH" ← ここで func が PATH に追加される
  ↓
(1行目はすでに実行済み。HOGE は空のまま変わらない)

正しい書き方:PATH を先に設定する

コマンド置換を使う export は、そのコマンドが PATH に入った後に書く。

# OK: PATH を先に設定してから使う
export PATH="$HOME/.local/bin:$PATH"

# mise が PATH に入っているのでコマンド置換が正常に動く
export MISE_ROOT=$(mise root)

よくある実例

Homebrew(macOS)

# NG
export HOMEBREW_PREFIX=$(brew --prefix)    # brew が PATH にない
eval "$(/opt/homebrew/bin/brew shellenv)"  # ここで brew が PATH に入る(遅い)

# OK
eval "$(/opt/homebrew/bin/brew shellenv)"  # 先に brew を PATH に追加
export HOMEBREW_PREFIX=$(brew --prefix)    # これで brew が使える

Go

# NG
export GOPATH=$(go env GOPATH)         # go が見つからない
export PATH="$PATH:/usr/local/go/bin"  # ここで go が PATH に入る(遅い)

# OK
export PATH="$PATH:/usr/local/go/bin"  # 先に go を PATH に追加
export GOPATH=$(go env GOPATH)         # これで go env が使える

pyenv / rbenv

# NG
eval "$(pyenv init -)"                  # pyenv が見つからない
export PATH="$HOME/.pyenv/bin:$PATH"   # ここで pyenv が PATH に入る(遅い)

# OK
export PATH="$HOME/.pyenv/bin:$PATH"   # 先に pyenv を PATH に追加
eval "$(pyenv init -)"                  # これで pyenv init が使える

絶対パスを使えば順番に依存しない

コマンドを絶対パスで指定すれば PATH に関係なく動作します。ただし環境依存になるため、インストール先が固定されているツールに限って使うのが無難です。

# 絶対パスなら順番に関係なく動く
export HOMEBREW_PREFIX=$(/opt/homebrew/bin/brew --prefix)

まとめ

パターン 結果
PATH に追加する$(cmd) を使う command not found / 変数が空になる
PATH に追加した$(cmd) を使う 正常に動作する
絶対パスで呼ぶ $(/usr/bin/cmd) 順番に関わらず動作する

.zshrc のデバッグには echo $PATH を途中に挟んで、その時点で PATH に何が入っているか確認するのが確実です。

mise を使ったバージョン管理については「miseでRubyをインストールする方法:.ruby-versionの使い方」も参照してください。

cargo-watch:ファイル変更時に自動ビルド・テストを実行する方法

cargo-watch:ファイル変更時に自動ビルド・テストを実行する方法

cargo-watch はファイルの変更を検知して、cargo buildcargo test などのコマンドを自動で再実行するツールです。コードを書き直すたびに手動でコマンドを実行する手間がなくなります。


インストール

cargo install cargo-watch

インストール後、cargo watch コマンドが使えるようになります。

cargo watch --version

基本の使い方

-x オプションで実行する cargo サブコマンドを指定します。

# ファイル変更時に cargo run を実行
cargo watch -x run

# ファイル変更時に cargo check を実行(型チェックのみ・高速)
cargo watch -x check

# ファイル変更時に cargo test を実行
cargo watch -x test

# ファイル変更時に cargo build を実行
cargo watch -x build

開発中は cargo watch -x check が最も手軽です。cargo run より高速で、型エラーや文法エラーをすぐに確認できます。


複数コマンドを連続実行する

-x を複数並べると、順番に実行されます。前のコマンドが失敗すると後続のコマンドは実行されません。

# check が通ったら test を実行
cargo watch -x check -x test

# check → test → run
cargo watch -x check -x test -x run

監視対象を絞る

デフォルトはプロジェクト全体を監視します。-w で対象ディレクトリを指定したり、-i で除外したりできます。

# src/ ディレクトリのみ監視
cargo watch -x check -w src/

# tests/ も含めて監視
cargo watch -x test -w src/ -w tests/

# target/ を除外(デフォルトで除外されているので通常不要)
cargo watch -x build -i target/

実行前にターミナルをクリアする

-c オプションをつけると、コマンドを実行するたびにターミナルをクリアします。出力が見やすくなります。

cargo watch -c -x check
cargo watch -c -x test

まとめ

コマンド 説明
cargo watch -x run ファイル変更時に自動で実行
cargo watch -x check 型チェックのみ・高速で確認
cargo watch -x test ファイル変更時に自動でテスト
cargo watch -x check -x test check が通ったら test を実行
cargo watch -w src/ 監視対象ディレクトリを指定
cargo watch -c -x check 実行前にターミナルをクリア

cargo-watch を使うことで、保存するたびにエラーをすぐ確認できる開発体験になります。クレートの追加方法については「cargo addでfeaturesを指定する方法:cargo-editなしで使える」も参照してください。

mise で Node.js をインストールする方法:.nvmrc の使い方

mise で Node.js をインストールする方法:.nvmrc の使い方

mise はバージョン管理ツールで、Node.js を含む複数の言語を一元管理できます。Ruby や Python をすでに mise で管理しているなら、Node.js も mise に統一することで .tool-versionsmise.toml ひとつですべてのバージョンを管理できます。この記事では mise を使った Node.js のインストールから、.nvmrc との連携までを解説します。


Node.js をインストールする

# 最新の LTS をインストール
mise install node

# メジャーバージョンを指定してインストール
mise install node@22
mise install node@20

# バージョンを完全に指定してインストール
mise install node@20.11.0

インストール後、node コマンドが使えるようになります。

node --version
# => v22.x.x

インストール済みバージョンを確認する

# インストール済みの Node.js 一覧
mise list node

# 現在有効なバージョン
mise current node

# インストール可能なバージョン一覧
mise ls-remote node

プロジェクトのバージョンを固定する

mise use で固定(推奨)

# カレントディレクトリに固定(mise.toml に書き込まれる)
mise use node@22

# グローバルに固定
mise use --global node@22

mise.toml に以下のように記録されます。

# mise.toml
[tools]
node = "22"

このファイルをリポジトリにコミットしておくと、チームメンバーが mise install を実行するだけで同じバージョンが使えます。


.nvmrc ファイルとの連携

mise は .nvmrc ファイルを自動で読み込みます。nvm を使っていたプロジェクトをそのまま mise で使えます。

# .nvmrc ファイルを作成(nvm 互換)
echo "22" > .nvmrc
# .nvmrc に書かれたバージョンをインストールして有効化
mise install

.nvmrcmise.toml が両方ある場合は mise.toml が優先されます。


Ruby + Node.js をまとめて管理する

Rails プロジェクトなど Ruby と Node.js を両方使う場合は、mise.toml ひとつで管理できます。

# mise.toml
[tools]
ruby = "3.3.0"
node = "22"
# 両方まとめてインストール
mise install

nvm や rbenv を混在させる必要がなくなります。


バージョンを切り替える

# シェルセッション中だけ切り替え
mise shell node@20

# プロジェクトのバージョンを変更
mise use node@20

まとめ

コマンド 説明
mise install node 最新の LTS をインストール
mise install node@22 メジャーバージョンを指定してインストール
mise install node@20.11.0 バージョンを完全に指定してインストール
mise list node インストール済みバージョン一覧
mise current node 現在有効なバージョンを確認
mise use node@22 プロジェクトのバージョンを固定(mise.toml)
mise use --global node@22 グローバルバージョンを固定
mise shell node@20 セッション中だけ切り替え
mise install .nvmrc / mise.toml のバージョンをインストール

.nvmrc を使えば nvm からの移行もスムーズです。Node.js のバージョン確認コマンドの詳細は「Node.jsのバージョン確認方法:nvm・mise・npm・yarn・pnpm一覧」も参照してください。

Ruby も mise で管理している場合は「mise で Ruby をインストールする方法:.ruby-version の使い方」も参照してください。