一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第18天,点击查看活动详情。
本文的翻译于<<Effective TypeScript>>, 特别感谢!! ps: 本文会用简洁, 易懂的语言描述原书的所有要点. 如果能看懂这文章,将节省许多阅读时间. 如果看不懂,务必给我留言, 我回去修改.
技巧26: 代码上下文与类型推断
ts推断变量的类型不仅仅根据 values, 还根据变量所处的上下文. 所以我们需要理解上下文如何应用在类型推断上. 防止意外情况出现.
在js中, 你可以将一个表达式构造成一个常量: (也就是从 Inline 模式变成 Reference 模式)
// Inline form
setLanguage('JavaScript');
// Reference form
let language = 'JavaScript';
setLanguage(language);
在ts中这种重构依旧有效:
function setLanguage(language: string) { /* ... */ }
setLanguage('JavaScript'); // OK
let language = 'JavaScript';
setLanguage(language); // OK
但是如果你将language类型从string变成更精确的类型, 就会报错:
type Language = 'JavaScript' | 'TypeScript' | 'Python';
function setLanguage(language: Language) { /* ... */ }
setLanguage('JavaScript'); // OK
let language = 'JavaScript';
setLanguage(language);
// ~~~~~~~~ Argument of type 'string' is not assignable
// to parameter of type 'Language'
let language = 'JavaScript'; 这一句中, ts将language类型推断为string, 导致和函数参数的类型冲突.
有两个好的方法解决这个问题:
-
显示类型声明:
let language: Language = 'JavaScript'; setLanguage(language); // OK -
将let换成const:
const language = 'JavaScript'; setLanguage(language); // OK换了之后, language的类型推断为 'JavaScript', 而不是string(更多见技巧 21)
类似的还有其他数据类型也会出类似的问题:
-
tuple :
/ Parameter is a (latitude, longitude) pair. function panTo(where: [number, number]) { /* ... */ } panTo([10, 20]); // OK const loc = [10, 20]; panTo(loc); // ~~~ Argument of type 'number[]' is not assignable to // parameter of type '[number, number]'这里的
const loc = [10, 20];被推断为 number[] 和函数参数不兼容.解决办法
- 显示类型申明:
const loc: [number, number] = [10, 20]; panTo(loc); // OK - const 断言(as const):
这里报错是因为 loc被推断为:const loc = [10, 20] as const; panTo(loc); // ~~~ Type 'readonly [10, 20]' is 'readonly' // and cannot be assigned to the mutable type '[number, number]'readonly [10, 20], 而不是number[]. 主要是参数 readonly和函数参数产生冲突, 解决办法:但是const context 也有不足. 就是 在loc定义的时候出错,例如多加了一个元素. 就会产生报错.这个报错的地方是在loc使用的地方, 而不是loc定义的地方:function panTo(where: readonly [number, number]) { /* ... */ } const loc = [10, 20] as const; panTo(loc); // OKconst loc = [10, 20, 30] as const; // error is really here. panTo(loc); // ~~~ Argument of type 'readonly [10, 20, 30]' is not assignable to // parameter of type 'readonly [number, number]' // Types of property 'length' are incompatible // Type '3' is not assignable to type '2'
- 显示类型申明:
-
object 也会有类似的问题:
type Language = 'JavaScript' | 'TypeScript' | 'Python'; interface GovernedLanguage { language: Language; organization: string; } function complain(language: GovernedLanguage) { /* ... */ } complain({ language: 'TypeScript', organization: 'Microsoft' }); // OK const ts = { language: 'TypeScript', organization: 'Microsoft', }; complain(ts); // ~~ Argument of type '{ language: string; organization: string; }' // is not assignable to parameter of type 'GovernedLanguage' // Types of property 'language' are incompatible // Type 'string' is not assignable to type 'Language'解决方法也是一样: 1. 显示类型申明, 2. const断言
-
Callbacks 回调函数:
正常情况:
function callWithRandomNumbers(fn: (n1: number, n2: number) => void) { fn(Math.random(), Math.random()); } callWithRandomNumbers((a, b) => { a; // Type is number b; // Type is number console.log(a + b); });从 inline模式 转为 Reference 模式, 发现报错:
const fn = (a, b) => { // ~ Parameter 'a' implicitly has an 'any' type // ~ Parameter 'b' implicitly has an 'any' type console.log(a + b); } callWithRandomNumbers(fn);解决办法:
- 函数参数添加类型申明:
const fn = (a: number, b: number) => { console.log(a + b); } callWithRandomNumbers(fn);- 对整个函数添加类型声明: 见技巧 12