Classes and Interfaces
-
TSはC#からいろいろ借用している
- 可視性
- プロパティ初期化
- ポリモーフィズム
- デコレータ
- インタフェース
-
実行時はあくまでJSなので、ミックスインのようなJSイディオムもいける
- 型安全に
Classes and Inheritance
// 駒の色
type Color = 'Black' | 'White'
// 盤上の座標
type File = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H'
type Rank = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
class Game { }
class Position {
constructor(
private file: File, // this.fileへの代入のショートハンド
private rank: Rank
) { }
}
class Piece {
protected position: Position
constructor(
private readonly color: Color,
file: File,
rank: Rank
) {
this.position = new Position(file, rank)
}
}
class King extends Piece { }
class Queen extends Piece { }
class Bishop extends Piece { }
class Knight extends Piece { }
class Rook extends Piece { }
class Pawn extends Piece { }
- これはエラー
class Piece {
protected position: Position // Error: Property 'position' has no initializer and is not definitely assigned in the constructor.
constructor(
private readonly color: Color,
file: File,
rank: Rank
) {
// this.position = new Position(file, rank)
}
}
super
class Base {
protected foo() {
}
protected bar = 1
protected get baz() {
return 2
}
}
class Derived extends Base {
constructor() {
super()
super.foo()
super.bar
super.baz
}
}
- 書籍では「sueprでプロパティアクセスできない」と書いてあったが、コンパイルは通る
-
Call base class property via super
- スーパークラス(のコンストラクタ関数)のprototypeを指しているから意図した挙動にならない、ということか
Using this as a Return Type
class Base {
foo(): Base {
return this;
}
bar(): this {
return this;
}
}
class Derived extends Base {
}
const b = new Base
const bFoo = b.foo() // Base
const bBar = b.bar() // Base
const d = new Derived
const dFoo = d.foo() // Base
const dBar = d.bar() // Derived
-
PHPのDocコメントでいう
@return $this
にあたるやつ- newしたものは返せない (
@return static
とは違う)
- newしたものは返せない (
Interfaces
- typeとそっくりなやつ
type Food = {
calories: number
sweet: boolean
tasty: boolean
}
type Sushi = Food & {
salty: boolean
}
type Cake = Food & {
sweet: boolean
}
- interfaceで書くとこうなる
interface Food {
calories: number
sweet: boolean
tasty: boolean
}
interface Sushi extends Food {
salty: boolean
}
interface Cake extends Food {
sweet: boolean
}
- typeとinterfaceの違い
type | interface | |
---|---|---|
右辺に型の式を書ける | o | x. shapeのみ |
シグネチャが合わない交差/継承をすると | オーバーロード | エラー |
同一スコープに同一名定義 | エラー | declaration merge |
interface A {
good(x: number): string
bad(x: number): string
}
interface B extends A { // Error: Interface 'B' incorrectly extends interface 'A'.
good(x: string | number): string
bad(x: string): string
}
- typeではエラーにならない
type A = {
good(x: number): string
bad(x: number): string
}
type B = A & {
good(x: string | number): string
bad(x: string): string
}
Declaration Merging
interface User {
name: string
}
interface User {
age: number
}
const a: User = {
name: 'John',
age: 20
}
- 矛盾はだめ
interface User {
age: string
}
interface User {
age: number // Error: Subsequent property declarations must have the same type. Property 'age' must be of type 'string', but here has type 'number'.
}
- typeだとエラー
type User = { // Error: Duplicate identifier 'User'.
name: string
}
type User = { // Error: Duplicate identifier 'User'.
age: number
}
const a: User = {
name: 'John',
age: 20 // Error: Type '{ name: string; age: number; }' is not assignable to type 'User'.
}
- 名前を変えて交差すれば通る
type User = { // Error: Duplicate identifier 'User'.
name: string
}
type User2 = User & {
age: number
}
const a: User2 = {
name: 'John',
age: 20
}
implementations
- classを定義する時にinterfaceを
implements
する -
interfaceでできること・できないこと
- できる
- readonlyプロパティ
- できない
- 可視性の指定
- static
Implementing Interfaces Versus Extending Abstract Classes
- interfaceはコンパイル時にのみ存在する
- abstract classはコンパイル後のJSコードに存在する
- 「interfaceでできないこと」はこのことに起因する