Item 18: Avoid Cluttering Your Code with Inferable Types
May 10, 2024 ยท View on GitHub
Things to Remember
- Avoid writing type annotations when TypeScript can infer the same type.
- Ideal TypeScript code has type annotations in function/method signatures but not on local variables in their bodies.
- Consider using explicit annotations for object literals to enable excess property checking and ensure errors are reported close to where they occur.
- Don't annotate function return types unless the function has multiple returns, is part of a public API, or you want it to return a named type.
Code Samples
let x: number = 12;
let x = 12;
const person: {
name: string;
born: {
where: string;
when: string;
};
died: {
where: string;
when: string;
}
} = {
name: 'Sojourner Truth',
born: {
where: 'Swartekill, NY',
when: 'c.1797',
},
died: {
where: 'Battle Creek, MI',
when: 'Nov. 26, 1883'
}
};
const person = {
name: 'Sojourner Truth',
born: {
where: 'Swartekill, NY',
when: 'c.1797',
},
died: {
where: 'Battle Creek, MI',
when: 'Nov. 26, 1883'
}
};
function square(nums: number[]) {
return nums.map(x => x * x);
}
const squares = square([1, 2, 3, 4]);
// ^? const squares: number[]
const axis1: string = 'x';
// ^? const axis1: string
const axis2 = 'y';
// ^? const axis2: "y"
interface Product {
id: number;
name: string;
price: number;
}
function logProduct(product: Product) {
const id: number = product.id;
const name: string = product.name;
const price: number = product.price;
console.log(id, name, price);
}
interface Product {
id: string;
name: string;
price: number;
}
function logProduct(product: Product) {
const id: number = product.id;
// ~~ Type 'string' is not assignable to type 'number'
const name: string = product.name;
const price: number = product.price;
console.log(id, name, price);
}
function logProduct(product: Product) {
const {id, name, price} = product;
console.log(id, name, price);
}
function logProduct(product: Product) {
const {id, name, price}: {id: string; name: string; price: number } = product;
console.log(id, name, price);
}
function parseNumber(str: string, base=10) {
// ^? (parameter) base: number
// ...
}
// Don't do this:
app.get('/health', (request: express.Request, response: express.Response) => {
response.send('OK');
});
// Do this:
app.get('/health', (request, response) => {
// ^? (parameter) request: Request<...>
response.send('OK');
// ^? (parameter) response: Response<...>
});
const elmo: Product = {
name: 'Tickle Me Elmo',
id: '048188 627152',
price: 28.99,
};
const furby = {
name: 'Furby',
id: 630509430963,
price: 35,
};
logProduct(furby);
// ~~~~~ Argument ... is not assignable to parameter of type 'Product'
// Types of property 'id' are incompatible
// Type 'number' is not assignable to type 'string'
const furby: Product = {
name: 'Furby',
id: 630509430963,
// ~~ Type 'number' is not assignable to type 'string'
price: 35,
};
logProduct(furby);
function getQuote(ticker: string) {
return fetch(`https://quotes.example.com/?q=${ticker}`)
.then(response => response.json());
}
const cache: {[ticker: string]: number} = {};
function getQuote(ticker: string) {
if (ticker in cache) {
return cache[ticker];
}
return fetch(`https://quotes.example.com/?q=${ticker}`)
.then(response => response.json())
.then(quote => {
cache[ticker] = quote;
return quote as number;
});
}
getQuote;
// ^? function getQuote(ticker: string): number | Promise<number>
getQuote('MSFT').then(considerBuying);
// ~~~~ Property 'then' does not exist on type
// 'number | Promise<number>'
const cache: {[ticker: string]: number} = {};
function getQuote(ticker: string): Promise<number> {
if (ticker in cache) {
return cache[ticker];
// ~~~ Type 'number' is not assignable to type 'Promise<number>'
}
// ...
}
interface Vector2D { x: number; y: number; }
function add(a: Vector2D, b: Vector2D) {
return { x: a.x + b.x, y: a.y + b.y };
}