《Effective TypeScript》条款21 - 类型扩展


本文主要通过一些实际的代码示例,来帮助大家理解什么是类型扩展,本文主要内容如下:

  1. 什么是类型扩展
  2. 代码示例
  3. 总结

什么是类型扩展?

TypeScript 需要从你指定的单一值中决定一组可能的值,这个过程成为类型扩展

代码示例

interface Vector { x: number; y: number; z: number; }
function getComponent(vector: Vector, axis: keyof Vector) { return vector[axis]; }
当你使用它时,TypeScript会提示一个错误.

//~类型string的参数不能赋值给类型 "x"|"y"|"z"的参数

报错的原因在于,x被推断为string,而getComponent的第二个参数则是期望得到一个更加具体的单位类型
这就是类型扩展在起作用,所以导致这里产生了一个错误。

x被推断为string,因为typescript允许这样的代码:
let x = 'x';x = 'a'; x = 'b';
typescript试图在特殊性和灵活性之间找到平衡,一般的规则是,一个变量的类型不应该在它被声明后改变,所以string 比 string|Regexp或者string|string[]或者any更合理。

TypeScript给了一些方法控制类型扩展过程,其中一种就是const,如果你用const而不是let,var来声明变量,就会得到一个更窄的类型。
const x = "x"; //x的类型为"x",而不是string;
然而const对数组和对象来说仍然无效,因为在js中,对象自身不变的情况下,对象里面的属性是可以改变的。
比如:
const v = {x:1};
v的类型可以被推断到任意程度,最具体的为:{readonly x :1},一般化的是{x:number},再一般化的就是{[key:string]:number}或者object了。
对于对象来说,Typescript 的类型扩展算法会把对象中的每个元素当作是用let赋值了,所以,const v = {x:1}
的类型 就是 {x:number};
我们可以通过Typescript 提供的方法来覆盖Typescript 的默认行为。

  1. 提供一个明确的类型标注 const v:{x:1|3|5} = {x:1}
  2. 为类型检查器提供额外的上下文 通过传递值作为函数的参数
  3. 使用const断言 // const断言是纯粹的运行在类型空间的东西
    const v1 = {x:1,y:1};//类型为{x:number,y:number}
    const v2 = {x:1 as const,y:1};//类型为 {x:1,y:number}
    const v3 = {x:1,y:2} as const; //类型为 {x:readonly x :number,readonly y:number}

当你在一个值后面写as const的时候,TypeScript会把它推断成最窄的类型.
更鲜明的例子:
const a1 = [1,2,3];//类型为 number[]
const a2 = [1,2,3] as const;//类型为readonly [1,2,3]

总结

  • 了解TypeScript是如何通过扩展常量来推断类型的
  • 熟悉影响这种行为的方法:const , 上下文,和 as const注释。