Item 30: Be Liberal in What You Accept and Strict in What You Produce
Input types tend to be broader than output types. Optional properties and union types are more common in parameter types than return types.
Avoid broad return types since these will be awkward for clients to use.
To reuse types between parameters and return types, introduce a canonical form (for return types) and a looser form (for parameters).
Use Iterable<T>
instead of T[]
if you only need to iterate over your function parameter.
declare function setCamera ( camera : CameraOptions ) : void ;
declare function viewportForBounds ( bounds : LngLatBounds ) : CameraOptions ;
💻 playground
interface CameraOptions {
center ?: LngLat ;
zoom ?: number ;
bearing ?: number ;
pitch ?: number ;
}
type LngLat =
{ lng : number ; lat : number ; } |
{ lon : number ; lat : number ; } |
[ number , number ] ;
💻 playground
type LngLatBounds =
{ northeast : LngLat , southwest : LngLat } |
[ LngLat , LngLat ] |
[ number , number , number , number ] ;
💻 playground
function focusOnFeature ( f : Feature ) {
const bounds = calculateBoundingBox ( f ) ; // helper function
const camera = viewportForBounds ( bounds ) ;
setCamera ( camera ) ;
const { center : { lat, lng} , zoom} = camera ;
// ~~~ Property 'lat' does not exist on type ...
// ~~~ Property 'lng' does not exist on type ...
zoom ;
// ^? const zoom: number | undefined
window . location . search = `?v=@${ lat } ,${ lng } z${ zoom } ` ;
}
💻 playground
interface LngLat { lng : number ; lat : number ; } ;
type LngLatLike = LngLat | { lon : number ; lat : number ; } | [ number , number ] ;
interface Camera {
center : LngLat ;
zoom : number ;
bearing : number ;
pitch : number ;
}
interface CameraOptions extends Omit < Partial < Camera > , 'center' > {
center ?: LngLatLike ;
}
type LngLatBounds =
{ northeast : LngLatLike , southwest : LngLatLike } |
[ LngLatLike , LngLatLike ] |
[ number , number , number , number ] ;
declare function setCamera ( camera : CameraOptions ) : void ;
declare function viewportForBounds ( bounds : LngLatBounds ) : Camera ;
💻 playground
interface CameraOptions {
center ?: LngLatLike ;
zoom ?: number ;
bearing ?: number ;
pitch ?: number ;
}
💻 playground
function focusOnFeature ( f : Feature ) {
const bounds = calculateBoundingBox ( f ) ;
const camera = viewportForBounds ( bounds ) ;
setCamera ( camera ) ;
const { center : { lat, lng} , zoom} = camera ; // OK
// ^? const zoom: number
window . location . search = `?v=@${ lat } ,${ lng } z${ zoom } ` ;
}
💻 playground
function sum ( xs : number [ ] ) : number {
let sum = 0 ;
for ( const x of xs ) {
sum += x ;
}
return sum ;
}
💻 playground
function sum ( xs : Iterable < number > ) : number {
let sum = 0 ;
for ( const x of xs ) {
sum += x ;
}
return sum ;
}
💻 playground
const six = sum ( [ 1 , 2 , 3 ] ) ;
// ^? const six: number
💻 playground
function * range ( limit : number ) {
for ( let i = 0 ; i < limit ; i ++ ) {
yield i ;
}
}
const zeroToNine = range ( 10 ) ;
// ^? const zeroToNine: Generator<number, void, unknown>
const fortyFive = sum ( zeroToNine ) ; // ok, result is 45
💻 playground