勉強日記

チラ裏

Programming TypeScript ch4 (1/2)

www.oreilly.com


Declaring And Invoking Functions

  • 語彙
    • parameter
      • 関数の動作に必要なもの
      • 仮引数
    • argument
      • 関数の起動に必要なもの
      • 実引数

Optional and Default Parameters

  • オプショナル引数よりもデフォルト引数を使おう
    • 末尾以外でも使える
    • 型推論が効く

Rest Parameters

  • 語彙
    • fixed-arity
      • 固定長引数の
    • variadic
      • 任意長引数の
      • 【補】引数の数に応じて monadic, diadic, triadic, ... と呼んだりする
  • 固定長ではなく任意長引数にしたいことがある
  • argumentsは型安全じゃないのでやめよう
function sum() {
  return Array
    .from(arguments)
    .reduce((total, n) => total + n, 0)
} // any

sum(1, 2, 3)  // Error: Expected 0 arguments, but got 3.
  • こうする
function sum(...numbers: number[]) {
  return numbers
    .reduce((total, n) => total + n, 0)
} // return type numberも推論される

sum(1, 2, 3)
  • 【補】最低引数長の指定可
function sum(...numbers: [number, ...number[]]) {
  return numbers
    .reduce((total, n) => total + n, 0)
}

sum()  // Error: Expected at least 2 arguments, but got 0.
sum(1)
sum(1,2)

call, apply, and bind

TSC Flag: strictBindCallApply

function withUnit(num: number, unit: string) {
  return `${num} ${unit}`;
}

withUnit(10, 'cm');
withUnit.call(null, 10, 20);      // Error: Argument of type '20' is not assignable to parameter of type 'string'.
withUnit.call(null, 10, 'cm');
withUnit.apply(null, [10, 20]);   // Error: Type 'number' is not assignable to type 'string'.
withUnit.apply(null, [10, 'cm']);
withUnit.bind(null, 10, 20)();    // Error: No overload matches this call.
withUnit.bind(null, 10, 'cm')();

Typing this

TSC Flag: noImplicitThis

  • thisはメソッド呼び出しのときにセットされる
const x = {
  a() {
    return this
  }
}

console.log(x.a())     // { a: [Function: a] }

const a = x.a
console.log(a())       // undefined

console.log(a.call(x)) // { a: [Function: a] }
  • thisのセットし忘れを取り締まれる
function month(this: Date) {
  return this.getMonth()
}

month();  // Error: The 'this' context of type 'void' is not assignable to method's 'this' of type 'Date'.
month.call(new Date);

Generator Functions

  • 無限リストとかを実現できるやつ
function* createFibonacciGenerator() {
  let a = 0
  let b = 1

  for (; ;) {
    yield a;
    [a, b] = [b, a + b]
  }
}

const fibonacciGenerator = createFibonacciGenerator() // Generator<number, void, unknown>

console.log(fibonacciGenerator.next()) // IteratorResult<number, void>
console.log(fibonacciGenerator.next())
console.log(fibonacciGenerator.next())

Iterators

  • Generatorの異なる側面
    • Generatorは値のストリームを生成する方法
    • Iteratorはその値を消費する方法
  • iterable
    • Symbol.iteratorプロパティを持つ任意のオブジェクト
  • iterator
    • nextメソッドをもつ任意のオブジェクト
      • valueプロパティとdoneプロパティを持つオブジェクトを返す
      • 先の例でいうIteratorResult<number, void>
    • ジェネレータ関数の戻り値がこれ
      • 先の例でいうGenerator<number, void, unknown>
const xs = [1, 2, 3]; // Array is iterable

const iteratorCreater = xs[Symbol.iterator] // () => IterableIterator<number>
const iterator = iteratorCreater()          // IterableIterator<number>
const result = iterator.next()              // IteratorResult<number, any>
  • iterableでできること
    • for-of
    • スプレッド演算
    • 分割代入

TSC Flag: downlevelIteration

  • ES2015よりも古い環境でカスタムイテレータを動かすためのフラグ
  • 無効にすることでバンドルサイズを抑えられる

Call Signatures

const add = function(a: number, b: number) {
  return a + b
} // (a: number, b: number) => number
let add: (apple: number, banana: number) => number;

add = function(a: number, b: number) {
  return a + b
}

Contextual Typing

function times(
  f: (index: number) => void,
  n: number
) {
  for (let i = 0; i < n; i++) {
    f(i)
  }
}

times(n => console.log(n), 4)
  • timesf: (index: number) => voidを要求しているので、コールバックが(n: number) => voidに推論される
  • インラインで定義しないとこの推論は行われない
function times(
  f: (index: number) => void,
  n: number
) {
  for (let i = 0; i < n; i++) {
    f(i)
  }
}

const log = n => console.log(n) // Error: Parameter 'n' implicitly has an 'any' type.

times(log, 4)

Overloaded Function Types

type Reservation = object

type Reserve = (from: Date, to: Date, destination: string) => Reservation
  • これは下記の型定義と等価
type Reservation = object

type Reserve = {
  (from: Date, to: Date, destination: string): Reservation
}
  • 後者の書き方で関数のオーバロードを表現できる
type Reservation = object

type Reserve = {
  (from: Date, to: Date, destination: string): Reservation
  (from: Date, destination: string): Reservation
}

const reserve: Reserve = (
  from: Date,
  toOrDestination: Date | string,
  destination?: string
) => {
  // ...
  return {}
}
  • 関数宣言をオーバロードしたいときはこう
function createElement(tag: 'a'): HTMLAnchorElement
function createElement(tag: 'canvas'): HTMLCanvasElement
function createElement(tag: 'table'): HTMLTableElement
function createElement(tag: 'a' | 'canvas' | 'table') {
  if (tag === 'a') return new HTMLAnchorElement
  if (tag === 'canvas') return new HTMLCanvasElement
  if (tag === 'table') return new HTMLTableElement

  const n: never = tag
  return n
}


const anchor = createElement('a')
const canvas = createElement('canvas')
const table = createElement('table')

Column: Keeping Overload Signatures Specific

const reserve: Reserve = (
  from: Date,
  toOrDestination: any,
  destination?: string
) => {
  // ...
  return {}
}
  • こういうのはやめようね、という話

英語

  • terse
    • 簡潔な
  • flip side
    • 異なる側面