boolean,number,string
let value: boolean = false;
let count: number = 0;
let inputText:string = 'hello';
オブジェクト
const person: {
name: string,
age: number
} = {
name: 'Sadako',
age: 25
}
配列
const Vegetable: string[] = ['Potato','Carrot','Eggplant'];
const numbers: number[] = [1, 2, 3, 4, 5];
const booleans: boolean[] = [true, false, true];
Arrayクラスのジェネリクスを使用して書くこともできます。
const numbers: Array<number> = [1, 2, 3, 4, 5];
カスタム型の配列
type TodoType = { title: string; completed: boolean };
const todos: TodoType[] = [{ title: "Learn TypeScript", completed: false }];
const todos: Array<TodoType> = [
{ title: "Learn TypeScript", completed: false }
];
レストパラメータ
function sum(...numbers: number[]): number {
return numbers.reduce((acc, num) => acc + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 出力: 10
function logItems(...items: string[]): void {
console.log(items);
}
logItems('apple', 'banana', 'cherry'); // 出力: ['apple', 'banana', 'cherry']
↑このようにレストパラメータによって、引数を複数渡せます。
これがレストパラメータでなかった場合、
function printPersonInfo(personInfo: [string, number]) {
const [name, age] = personInfo;
console.log(`Name: ${name}, Age: ${age}`);
}
printPersonInfo(["Alice", 30]); // ✅ OK!
printPersonInfo("Alice", 30); // ❌ エラー!
引数は配列1つだけで[“Alice”, 30]という形で渡さないといけないです。
Tuple型
異なる型を順番に持つ配列を表現します。
const book: [string, number, boolean] = ['philosophy', 2000, false];
Enum型
列挙型
Enum型は数値・文字列の相互変換が可能です。
enum Direction {
Up = 1,
Down,
Left,
Right
}
let dir: Direction = Direction.Up;
使用例
フォームのステップ管理
enum Step {
Input,
Confirm,
Complete
}
function FormComponent() {
const [step, setStep] = useState<Step>(Step.Input);
return (
<div>
{step === Step.Input && <p>入力画面</p>}
{step === Step.Confirm && <p>確認画面</p>}
{step === Step.Complete && <p>完了画面</p>}
<button onClick={() => setStep(step + 1)}>次へ</button>
</div>
);
}
APIの取得ステータス管理
enum Status {
Idle,
Loading,
Success,
Error
}
function DataFetcher() {
const [status, setStatus] = useState<Status>(Status.Idle);
useEffect(() => {
setStatus(Status.Loading);
fetch("/api/data")
.then(res => res.json())
.then(() => setStatus(Status.Success))
.catch(() => setStatus(Status.Error));
}, []);
if (status === Status.Loading) return <p>読み込み中...</p>;
if (status === Status.Error) return <p>エラーが発生しました</p>;
return <p>データ取得完了</p>;
}
タブ管理(switchで処理)
enum Tab {
Home,
Profile,
Settings
}
function Tabs() {
const [tab, setTab] = useState<Tab>(Tab.Home);
function renderTab() {
switch (tab) {
case Tab.Home:
return <p>ホーム</p>;
case Tab.Profile:
return <p>プロフィール</p>;
case Tab.Settings:
return <p>設定</p>;
}
}
return (
<div>
<button onClick={() => setTab(Tab.Home)}>ホーム</button>
<button onClick={() => setTab(Tab.Profile)}>プロフィール</button>
<button onClick={() => setTab(Tab.Settings)}>設定</button>
{renderTab()}
</div>
);
}
Union型
let box: number | string = 10;
配列でもUnion型を使用できます。
let boxFunction:(number | string)[] = [6, 'こんにちは'];
Literal型
リテラル型は値をそのまま型として扱うものです。
const orange = 'orange';
ユニオン型と合わせても使えます。
let shoesSize: 'small' | 'medium' | 'large' = 'small';
as const
as constはリテラル型として値を固定(読み取り専用)にします。
const color = "blue" as const;
↑colorの型はstringではなく”blue”(文字列リテラル型)になります。
typeエイリアスとInterface
typeエイリアスもInterfaceもどちらも型を定義するために使い、javascriptでは表示されません。
違いはこちら↓
比較項目 | interface | type エイリアス |
---|---|---|
拡張性 | extends で拡張できる | & (交差型)で合成する |
再定義 | 可能(自動的にマージされる) | 不可(同名定義はエラー) |
ユニオン型 | 不可 | 可能 |
プリミティブ型の代入 | 不可(基本的にオブジェクト専用) | 可能(stringやnumberなどもOK) |
使う場面の慣習 | ライブラリやAPIのオブジェクト定義 | 複雑な型(ユニオン、タプル、条件型など) |
interfaceの例
interface User {
name: string;
age: number;
}
interface Admin extends User {
role: string;
}
typeの例
type User = {
name: string;
age: number;
};
type Admin = User & {
role: string;
};
type ID = string | number; // ユニオン型OK
関数型のReactではtypeが主流で、interfaceはclassと組み合わせて使用される場合が多い。
implements
implementsはメソッドやプロパティを必ず持たせるようにします。
abstractとの違い↓
比較項目 | interface | abstract class |
---|---|---|
目的 | 形の定義(構造) | 形 + 一部実装 |
プロパティ定義 | ✅ OK | ✅ OK |
メソッド定義 | ✅ OK(中身はダメ) | ✅ OK(中身あり・なし両方OK) |
メソッドの実装 | ❌ できない(構造だけ) | ✅ できる |
継承の方法 | implements | extends |
多重継承 | ✅ OK(複数interface実装できる) | ❌(1つのクラスしか継承できない) |
具体例↓
interface Flyable {
fly(): void;
}
interface Animal {
name: string;
speak(): void;
}
class Bird implements Animal, Flyable {//implementsはカンマで区切って複数書ける
name: string;
constructor(name: string) {
this.name = name;
}
speak() {
console.log(`${this.name} がさえずる`);
}
fly() {
console.log(`${this.name} が飛ぶ`);
}
}
class Dog implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
speak() {
console.log(`${this.name} がワンと吠える`);
}
}
abstractとの大きな違いは構造の定義だけか実装もできるかの違いですが、interfaceはextendsも使えます。(複数も可)
interface HasName {
name: string;
}
interface CanSpeak {
speak(): void;
}
interface Person extends HasName, CanSpeak {}
const me: Person = {
name: "山田",
speak() {
console.log("こんにちは!");
}
};
コールシグネチャ
関数の型を定義するためのinterfaceの書き方です。
interface GreetFunction {
(name: string): string;
}
const greet: GreetFunction = (name) => {
return `こんにちは、${name}さん`;
};
console.log(greet("山田")); // こんにちは、山田さん
コンストラクタシグネチャ
newできる関数(クラスなど)の型を定義します。
interface UserConstructor {
new (name: string): User;
}
class User {
constructor(public name: string) {}
}
function createUser(Ctor: UserConstructor, name: string): User {
return new Ctor(name);
}
const user = createUser(User, "佐藤");
console.log(user.name); // 佐藤
関数宣言に型をつける
パラメータには型を定義
戻り値(パラメータの型の後ろにコロンをつけて定義)は型推論でもOK
function caliculate(num1: number, num2: number): number{
return num1 + num2
}
関数式に型をつける
戻り値は=>で表します。
const caliculateA :(n1: number, n2: number) => number = caliculate;
void型
返り値がない場合に書きます。
function sayGoodbye(): void {
console.log('bye');
}
unknown型
型が不明な場合に使いますが、any
よりも安全です。unknown
型の値を操作する前に型チェックが必要です。
let value: unknown = 5;
if (typeof value === 'number') {
console.log(value + 1);
}
never型
値を何も返さない
type Animal = 'dog' | 'cat';
function sound(animal: Animal) {
switch (animal) {
case 'dog':
return 'wan wan';
case 'cat':
return 'nyan nyan';
default:
// `animal`は絶対に'dog'か'cat'なので、ここに来ることはないはず
const _exhaustiveCheck: never = animal;
return _exhaustiveCheck;
}
}
アロー関数に型をつける
const doubleNumber = num(num: number) => num * 2
または
const doubleNumber: (num: number) => number = num => num * 2;
コールバック関数に型をつける
function doMath(
a: number,
b: number,
callback: (x: number, y: number) => number
): number {
return callback(a, b);
}
クラスに型をつける
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(this: {name: string}) {
return `こんにちは、私は${this.name}です。`;
}
}
const taro = new Person("太郎", 30);
console.log(taro.greet()); // => こんにちは、私は太郎です。
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
greet(this: Person): void {
console.log(`こんにちは、私は${this.name}です`);
}
}
const p = new Person("田中");
p.greet(); // OK
インターセクション型
複数の型を&(アンパサンド)で結合して定義します。
type A = { name: string };
type B = { age: number };
type Person = A & B;
const p: Person = {
name: "山田",
age: 30
};//Personはnameとageの両方を持つ必要がある
複数の構造を組み合わせる↓
type HasId = { id: number };
type HasTimestamp = { createdAt: Date };
type Record = HasId & HasTimestamp;
const log: Record = {
id: 123,
createdAt: new Date()
};
ジェネリクス
ジェネリクスは型をパラメータとして受け取る仕組みです。
function identity<T>(value: T): T {
return value;
}
console.log(identity(123)); // 123 (number)
console.log(identity("abc")); // "abc" (string)
タイプガード
型を判別するためのif文などの中での判定方法で、型に応じてコードの振る舞いを変える時に使います。
typeof
function printId(id: number | string) {
if (typeof id === "string") {
console.log("文字列ID:", id.toUpperCase());
} else {
console.log("数値ID:", id.toFixed(2));
}
}
in演算子(プロパティの有無)
type Dog = { bark: () => void };
type Cat = { meow: () => void };
function handleAnimal(animal: Dog | Cat) {
if ("bark" in animal) {
animal.bark();
} else {
animal.meow();
}
}
instanceof(クラスインスタンスの判定)
instanceofはクラスインスタンスにしか使えません。
class Car {
drive() { console.log("ブーン"); }
}
class Bike {
pedal() { console.log("シャカシャカ"); }
}
function move(vehicle: Car | Bike) {
if (vehicle instanceof Car) {
vehicle.drive();
} else {
vehicle.pedal();
}
}
ユーザー定義タイプガード(関数で判定)
type Fish = { swim: () => void };//swimは関数型のプロパティ
type Bird = { fly: () => void };
function isFish(pet: Fish | Bird): pet is Fish {
return "swim" in pet;
}
function handlePet(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim(); // Fish と確定されて安全に使える!
} else {
pet.fly();
}
}
pet is Fish(ユーザー定義タイプガード)がTypeScriptの構文です。
タグ付きユニオン
共通の識別子(= タグ)を持ったユニオン型で、これにより型を安全に絞り込めます。
タグ付きユニオン型の例↓(kindという共通プロパティがタグ)
type Circle = {
kind: "circle";
radius: number;
};
type Square = {
kind: "square";
sideLength: number;
};
type Shape = Circle | Square;
これを使ってタグ付きユニオンを使う関数↓
function getArea(shape: Shape): number {
if (shape.kind === "circle") {
return Math.PI * shape.radius ** 2;
} else {
return shape.sideLength ** 2;
}
}
非同期の状態管理にも使えます。
type Loading = { state: "loading" };
type Success = { state: "success"; data: string };
type Error = { state: "error"; error: string };
type FetchState = Loading | Success | Error;
function render(state: FetchState) {
switch (state.state) {
case "loading":
return "読み込み中...";
case "success":
return `データ: ${state.data}`;
case "error":
return `エラー: ${state.error}`;
}
}
インデックスシグネチャ
キーの名前が不定だけど、型は同じなオブジェクトに対して型定義をします。
書き方
interface 型名 {
[キー名: キーの型]: 値の型;
}
使用例
type UserScores = {
[username: string]: number;
};
const scores: UserScores = {
alice: 95,
bob: 82,
charlie: 78,
};
Lookup型
// Lookup 型の定義
type Lookup<T, K extends keyof T> = T[K];//Tというオブジェクトの中からキーKに対応する型を取り出す。
// Person 型の定義
type Person = {
name: string;
age: number;
isAdmin: boolean;
};
// Lookup 型を使用して、特定のプロパティの型を抽出
type NameType = Lookup<Person, 'name'>; // => string
type AgeType = Lookup<Person, 'age'>; // => number
インデックス型アクセス
type Person = {
name: string;
age: number;
isAdmin: boolean;
};
// インデックス型アクセスを使って特定のプロパティの型を取得
type NameType = Person['name']; // => string
type AgeType = Person['age']; // => number
TypeScriptでプロパティ名を型として取得するにはクオーテーションが必要です。