Programming TypeScript ch5 -- (1/2)

TypeScript勉強メモ

出典: 


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とは違う)

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でできないこと」はこのことに起因する