Item 23: Be Consistent in Your Use of Aliases
May 10, 2024 ยท View on GitHub
Things to Remember
- Aliasing can prevent TypeScript from narrowing types. If you create an alias for a variable, use it consistently.
- Be aware of how function calls can invalidate type refinements on properties. Trust refinements on local variables more than on properties.
Code Samples
const place = {name: 'New York', latLng: [41.6868, -74.2692]};
const loc = place.latLng;
interface Coordinate {
x: number;
y: number;
}
interface BoundingBox {
x: [number, number];
y: [number, number];
}
interface Polygon {
exterior: Coordinate[];
holes: Coordinate[][];
bbox?: BoundingBox;
}
function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
if (polygon.bbox) {
if (pt.x < polygon.bbox.x[0] || pt.x > polygon.bbox.x[1] ||
pt.y < polygon.bbox.y[0] || pt.y > polygon.bbox.y[1]) {
return false;
}
}
// ... more complex check
}
function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
const box = polygon.bbox;
if (polygon.bbox) {
if (pt.x < box.x[0] || pt.x > box.x[1] ||
// ~~~ ~~~ 'box' is possibly 'undefined'
pt.y < box.y[0] || pt.y > box.y[1]) {
// ~~~ ~~~ 'box' is possibly 'undefined'
return false;
}
}
// ...
}
function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
polygon.bbox
// ^? (property) Polygon.bbox?: BoundingBox | undefined
const box = polygon.bbox;
// ^? const box: BoundingBox | undefined
if (polygon.bbox) {
console.log(polygon.bbox);
// ^? (property) Polygon.bbox?: BoundingBox
console.log(box);
// ^? const box: BoundingBox | undefined
}
}
function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
const box = polygon.bbox;
if (box) {
if (pt.x < box.x[0] || pt.x > box.x[1] ||
pt.y < box.y[0] || pt.y > box.y[1]) { // OK
return false;
}
}
// ...
}
function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
const {bbox} = polygon;
if (bbox) {
const {x, y} = bbox;
if (pt.x < x[0] || pt.x > x[1] || pt.y < y[0] || pt.y > y[1]) {
return false;
}
}
// ...
}
const {bbox} = polygon;
if (!bbox) {
calculatePolygonBbox(polygon); // Fills in polygon.bbox
// Now polygon.bbox and bbox refer to different values!
}
function expandABit(p: Polygon) { /* ... */ }
polygon.bbox
// ^? (property) Polygon.bbox?: BoundingBox | undefined
if (polygon.bbox) {
polygon.bbox
// ^? (property) Polygon.bbox?: BoundingBox
expandABit(polygon);
polygon.bbox
// ^? (property) Polygon.bbox?: BoundingBox
}