TL;DR
- 関数のシグネチャにも Postel の法則を当てはめ、入力(引数)の型は寛容に、出力(戻り値)の型は厳格にする。
- 入出力で型を再利用できるよう、出力用の正規の型と、入力用の寛容な型を用意するとよい。
内容
入力には寛容に、出力には厳格に
関数のシグネチャにも Postel の法則を適用できる。つまり、引数の型はより寛容に、戻り値の型はより厳格にする。戻り値の型が寛容だと(たとえばプロパティが省略可能だと)、呼び出し側で扱いにくい。この原則に従うと、オプションプロパティやユニオン型は、主に入力の型に現れるはずである。
正規の型と寛容な型を分ける
入出力で型を再利用するには、出力の型に使う正規の形式と、入力の型に使う寛容な形式を用意するとよい。
// 出力の型
interface LngLat {
lng: number
lat: number
}
interface Camera {
center: LngLat
zoom: number
bearing: number
pitch: number
}
// 入力の型
type LngLatLike = LngLat | { lon: number; lat: number } | [number, number]
interface CameraOptions extends Omit<Partial<Camera>, "center"> {
center?: LngLatLike
}function setCamera(camera: CameraOptions): void { ... }
function viewportForBounds(bounds: LngLatBounds): Camera { ... }反復処理するだけなら Iterable
入力値を反復処理するだけなら、Iterable<T> が最も寛容な型である(項目17)。
function sum(xs: Iterable<number>): number {
let sum = 0
for (const x of xs) {
sum += x
}
return sum
}Iterable<T> を受け取るようにすれば、ジェネレータも渡せる。
function* range(limit: number) {
for (let i = 0; i < limit; i++) {
yield i
}
}
const zeroToNine = range(10)
const fortyFive = sum(zeroToNine)キーワード
自分の理解
100 Go Mistakes and How to Avoid Them にも、同じ考え方の項目があった(インタフェースを返す (#7))。