Programming TypeScript ch6 (1/3)

TypeScript勉強メモ

出典: 


Relationships Between Types

Subtypes and Supertypes

  • 型A,Bがあり、AがBのsubtypeであるとき、Bが必要なところでAを安全に使える
  • 型A,Bがあり、AがBのsupertypeであるとき、Aが必要なところでBを安全に使える
  • 表記法の導入

    • A <: B … AはBのsubtypeまたは同一の型
    • A >: B … AはBのsupertypeまたは同一の型

Variance

  • ジェネリクスが絡むとそう簡単な話ではなくなってくる

    • A[]B[]のsubtypeであるか?
  • 言語によりけり
  • TSは…

    • 複雑な型はだいたい共変
    • 関数のパラメータだけは例外的に反変
type Base = {
  x: number
}
type Derived = Base & {
  y: string
}

type Bs = Base[]
type Ds = Derived[]


const b: Base = { x: 1 }
const d: Derived = { x: 2, y: 'z' }

const b2: Base = d

const bs: Bs = [b]
const ds: Ds = [d]
const bs2: Bs = ds    // !!! covariant

bs.push(b)
ds.push(b)  // Derived[]にBaseはpushできない
bs2.push(b) // Base[] (実際に入っているのはDerived[])にBaseをpushできてしまう
type Klass = {
  f: (n: number) => number
}

const foo: Klass = {
  f: (n: number | null): number => {
    return 1
  }
}
// 利用者はKlass型だと思って使うので、実際に入っているものの引数はKlassよりも広くないといけない

TSC Flag: strictFunctionTypes

  • 関数の引数はデフォルト双変
  • tsconfigで"strictFunctionTypes": true,を設定すると反変になる
type Base = {
  a: number
}
type Derived = Base & {
  b: string
}

type AcceptBase = {
  (b: Base): void
}
type AcceptDerived = {
  (d: Derived): void
}

const acceptBase: AcceptBase = (b: Base) => { }
const acceptDerived: AcceptDerived = (d: Derived) => { }

const acceptBase2: AcceptBase = acceptDerived // Error: Type 'AcceptDerived' is not assignable to type 'AcceptBase'.
const acceptDerived2: AcceptDerived = acceptBase

Assignability

  • enum以外の場合、下記のいずれかを満たせばAはBに代入可能

    • A <: B
    • A がany型
    • JSとの相互運用のための例外

      • any自体はunknownを除いてtop型
  • enumの場合、下記のいずれかを満たせばAはBに代入可能

    • Aがenum Bのメンバである
    • Bがnumber型のメンバを持ち、Aがnumberである
    • 数値enumの罠

Type Widening

const a = 'x'      // 'X'
const b = 3        // 3
const c = true     // true
const d = { x: 3 } // {x: number;}
enum E { X, Y, Z }
const e = E.X      // E.X

let a2 = 'x'       // string
let b2 = 3         // number
var c2 = true      // boolean
let e2 = E.X       // E
  • nullやundefinedで初期化されるとanyに拡大される
  • ただし、戻り値は狭い
function x() {
  let a = null // widened to any
  a = 3        // any
  a = 'b'      // any
  return a     // string
} // returns string

The const type

let a = { x: 3 }          // { x: number; }
const b = { x: 3 }        // { x: number; }
let c: { x: 3 }           // { x: 3; }
let d = { x: 3 } as const // { readonly x: 3;}
  • as constはネストの中身もreadonlyにする
let xs = [1, 2, 3]              // number[]
let ys = [1, 2, 3] as const     // readonly [1, 2, 3]
let zx = [1, { x: 2 }] as const // readonly [1, { readonly x: 2; }]

Excess porperty checking

  • 余計なメンバがあると怒るやつ
type Options = {
  url: string
  method: 'GET' | 'POST'
  data?: object
}

function call(options: Options) {

}

call({
  url: 'http://localhost/users',
  method: 'GET',
  dataa: {} // Error: Argument of type '{ url: string; method: "GET"; dataa: {}; }' is not assignable to parameter of type 'Options'.
})

Refinement

  • flow-basedの型推論

    • if, ?:, ||, switch等で狭まるやつ

Discriminated union types

type UserTextEvent = { value: string, target: HTMLInputElement }
type UserMouseEvent = { value: [number, number], target: HTMLElement }

type UserEvent = UserTextEvent | UserMouseEvent

function handle(event: UserEvent) {
  if (typeof event.value === 'string') {
    event // UserEvent (!)
    event.value // string
    event.target // HTMLInputElement | HTMLElement (!!!)
    return
  }

  event // UserEvent (!)
  event.value // [number, number]
  event.target // HTMLInputElement | HTMLElement (!!!)
}
  • 意図した挙動にしたいときはtagged unionにしよう
type UserTextEvent = { type: 'UserTextEvent', value: string, target: HTMLInputElement }
type UserMouseEvent = { type: 'UserMouseEvent', value: [number, number], target: HTMLElement }

type UserEvent = UserTextEvent | UserMouseEvent

function handle(event: UserEvent) {
  if (event.type === 'UserTextEvent') {
    event // UserTextEvent
    event.value // string
    event.target // HTMLInputElement
    return
  }

  event // UserMouseEvent
  event.value // [number, number]
  event.target // HTMLElement
}

英語

  • crotchetiest

    • crotchetyの最上級
  • crotchety

    • 奇狂な、気難しい