ゴール:extends で「共通部分をまとめる」感覚をつかむ
継承(extends)は、
「似たようなクラス同士で、共通部分を親クラスにまとめる」ための仕組みです。
ここでは、
「書き方」だけでなく「どういうときに使うと気持ちいいか」まで含めて、
初心者向けに丁寧にかみ砕いていきます。
継承とは何か:共通部分を“親クラス”に引き上げる
例えば、犬と猫のクラスを考えてみます。
class Dog {
name: string;
constructor(name: string) {
this.name = name;
}
move() {
console.log(`${this.name} が歩いた`);
}
bark() {
console.log("ワン!");
}
}
class Cat {
name: string;
constructor(name: string) {
this.name = name;
}
move() {
console.log(`${this.name} が歩いた`);
}
meow() {
console.log("ニャー!");
}
}
TypeScriptname と move が丸かぶりしています。
こういう「共通部分」を親クラスにまとめるのが継承です。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move() {
console.log(`${this.name} が歩いた`);
}
}
class Dog extends Animal {
bark() {
console.log("ワン!");
}
}
class Cat extends Animal {
meow() {
console.log("ニャー!");
}
}
const d = new Dog("ポチ");
d.move(); // Animal から継承
d.bark(); // Dog 独自
const c = new Cat("タマ");
c.move(); // Animal から継承
c.meow(); // Cat 独自
TypeScriptポイントは、「Dog も Cat も Animal という“共通の型”を持つ」ことです。extends は「A は B の一種だよ」という関係をコードで表現する記号だと思ってください。
extends の基本構文と型の関係
extends の書き方はとてもシンプルです。
class 親クラス名 {
// 共通のプロパティ・メソッド
}
class 子クラス名 extends 親クラス名 {
// 子クラス固有のプロパティ・メソッド
}
TypeScriptTypeScript 的には、
- 子クラスは「親クラスのすべての public / protected メンバー」を引き継ぐ
- 子クラスは「自分だけのメンバー」を追加できる
という関係になります。
型として見ると、
const a: Animal = new Dog("ポチ"); // OK
const b: Animal = new Cat("タマ"); // OK
TypeScriptのように、「Dog も Cat も Animal として扱える」ようになります。
これが「ポリモーフィズム(多態性)」の入り口ですが、
今は「共通の親型として扱える」とだけ理解しておけば十分です。
コンストラクタと super:親の初期化を呼び出す
子クラスにコンストラクタを定義するときは、super(...) が必須になります。
class Animal {
name: string;
constructor(name: string) {
console.log("Animal constructor");
this.name = name;
}
}
class Dog extends Animal {
constructor(name: string) {
console.log("Dog constructor start");
super(name); // 親クラスのコンストラクタを呼ぶ
console.log("Dog constructor end");
}
bark() {
console.log(`${this.name} がワンと鳴いた`);
}
}
const d = new Dog("ポチ");
d.bark();
TypeScriptここで重要なのは二つです。
一つ目は、「子クラスのコンストラクタでは、必ず最初に super(...) を呼ぶ」こと。super を呼ぶ前に this を触るとエラーになります。
二つ目は、「親クラスが受け取るべき情報(ここでは name)を、子クラスから super(name) で渡す」こと。
親クラスが「共通の初期化」を担当し、子クラスは「自分固有の初期化」を足していく、という役割分担になります。
メソッドの継承とオーバーライド
親クラスのメソッドは、そのまま子クラスで使えます。
class Animal {
constructor(public name: string) {}
move() {
console.log(`${this.name} が歩いた`);
}
}
class Dog extends Animal {
bark() {
console.log("ワン!");
}
}
const d = new Dog("ポチ");
d.move(); // 親クラスのメソッド
d.bark(); // 子クラスのメソッド
TypeScriptさらに、「親と同じ名前のメソッドを子クラスで書き直す(オーバーライド)」こともできます。
class Animal {
constructor(public name: string) {}
move() {
console.log(`${this.name} がトコトコ歩いた`);
}
}
class Bird extends Animal {
move() {
console.log(`${this.name} がバサバサ飛んだ`);
}
}
const a = new Animal("なぞの生き物");
a.move(); // なぞの生き物 がトコトコ歩いた
const b = new Bird("スズメ");
b.move(); // スズメ がバサバサ飛んだ
TypeScript同じ move() でも、
「どのクラスのインスタンスか」によって振る舞いが変わる、というのが継承の面白いところです。
ここで大事なのは、「型としてはどちらも Animal として扱える」ことです。
const animals: Animal[] = [
new Animal("なぞの生き物"),
new Bird("スズメ"),
];
for (const a of animals) {
a.move(); // 実際にはそれぞれのクラスの move が呼ばれる
}
TypeScript「共通のインターフェース(ここでは move())を持ちつつ、
中身の実装はクラスごとに変えられる」
これが継承の強みです。
いつ継承を使うか:extends を選ぶ基準
継承は便利ですが、乱用するとコードが絡まりやすくなります。
なので、基準を一つ持っておくといいです。
それは、
「A は B の一種と言えるか?」
(Dog は Animal の一種、Cat も Animal の一種)
と自分に問いかけることです。
「User は Logger の一種」みたいに、
日本語として違和感があるなら継承はやめておいたほうがいいです。
そういうときは「中に持つ(コンポジション)」のほうが自然です。
継承を使うときは、
- 共通のプロパティ・メソッドを親クラスにまとめたい
- 子クラスは「親クラスの一種」として自然に説明できる
- 親型としてまとめて扱いたい(
Animal[]にDogもCatも入れたい)
このあたりが揃っているかを意識してみてください。
まとめ:extends を自分の言葉で言うと
最後に、あなた自身の言葉でこう整理してみてください。
extends は、
「共通部分を親クラスに上げて、
子クラスは“親クラスの一種”として振る舞わせる仕組み」
書き方としては、
class 子 extends 親で継承関係を作る- 親のコンストラクタを呼ぶときは
super(...)を使う - 親のメソッドはそのまま使えるし、必要なら同じ名前で書き直せる
今のあなたのコードの中から、
「明らかに似たクラスが2つ以上ある」場所を一つ探してみてください。
それを「共通部分(親)+違い(子)」に分解して、extends を使って書き直してみると、
継承の気持ちよさがかなり実感できるはずです。

