一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情。
本文的翻译于<<Effective TypeScript>>, 特别感谢!! ps: 本文会用简洁, 易懂的语言描述原书的所有要点. 如果能看懂这文章,将节省许多阅读时间. 如果看不懂,务必给我留言, 我回去修改.
技巧19: 避免 类型推断扰乱你的代码
ts新手往往会给所有变量写类型申明.但是很多类型申明是没必要的: 例如不要这样写:
let x: number = 12;
直接这样写:
let x = 12;
如果把鼠标悬浮在x上面, 就会显示(需要ide支持):
这种简单类型变量的申明是多余的. 如果不确定ts的类型推断, 可以上面的方法试一试. ts也可以推断复杂类型: 不要如下面这么写:
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'
}
};
同样的, 在这写类型申明也是多余的. 和object类似的, array也是一样:
function square(nums: number[]) {
return nums.map(x => x * x);
}
const squares = square([1, 2, 3, 4]); // Type is number[]
有的时候, ts 的类型推荐比你期待的更精确:
const axis1: string = 'x'; // Type is string
const axis2 = 'y'; // Type is "y"
运行类型推断还能有利于重构. 比如你有一个Product 的类型, 和一个函数去log 它:
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);
}
当你想把 Product 的 id 类型从number改为string.发现报错:
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);
}
当你把 logProduct里面的类型申明删除就不会有这个报错:
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);
}
当ts无法根据上下文推断变量类型, 显示类型注释的版本是有用的.有一种常见情况: 函数参数. 有些语言会根据函数最终用途, 来推断函数参数类型, 但是ts不会.
在ts中, 变量类型在其第一次被引入的时候确认了. 理想的ts函数拥有类型签名而不是内部的局部变量. 能让读者专注于实现函数的逻辑.
有几种情况函数参数不需要显示类型注释:
-
函数参数有默认值:
function parseNumber(str: string, base=10) { // ... } -
函数作为某些库的回调函数, 这些库需要有类型注释:
/ Don't do this: app.get('/health', (request: express.Request, response: express.Response) => { response.send('OK'); }); // Do this: app.get('/health', (request, response) => { response.send('OK'); });
有几种情况, 你需要显示类型注释:
- 定义object时;
这样能执行类型检查, 尽早捕捉类型错误.特别当你有用可选属性.如果没有显示类型注释, elmo会在使用时报错, 而不是定义的时候报错 :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); - 函数返回值: 相同的, 当拥有显示类型注释,就能在函数实现的时候发现错误, 而不是函数使用的时候. 假如你有一个函数用来查询股票价格:
你决定增加缓存cache, 避免重复请求:function getQuote(ticker: string) { return fetch(`https://quotes.example.com/?q=${ticker}`) .then(response => response.json()); }函数体里面有两种类型的返回值, 当调用 getQuote, 可能会出错: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; }); }当你显示标注了返回值:那么就能在正确的地方报错:getQuote('MSFT').then(considerBuying); // ~~~~ Property 'then' does not exist on type // 'number | Promise<any>' // Property 'then' does not exist on type 'number'写清楚返回类型能帮助你在实现之前思考: 输入输出类型是什么? 实现的代码可能会改变, 但是输入输出类型一般不会变.这有点像 TDD(测试驱动编程)const cache: {[ticker: string]: number} = {}; function getQuote(ticker: string): Promise<number> { if (ticker in cache) { return cache[ticker]; // ~~~~~~~~~~~~~ Type 'number' is not assignable to 'Promise<number>' } // ... } - 当ts无法正确推断的时候:
你想要ts能推断出返回值类型为: Vector2D. 但是ts却推断出:interface Vector2D { x: number; y: number; } function add(a: Vector2D, b: Vector2D) { return { x: a.x + b.x, y: a.y + b.y }; }{ x: number; y: number; }