typescript

Ts字面量类型

2021-08-23  本文已影响0人  ableF

在 TypeScript 中,类型标注声明是在变量之后(即类型后置),它不像 Java 语言一样,先声明变量的类型,再声明变量的名称。
使用类型标注后置的好处是编译器可以通过代码所在的上下文推导其对应的类型,无须再声明变量类型。在 TypeScript 中,具有初始化值的变量、有默认值的函数参数、函数返回的类型都可以根据上下文推断出来。
正是得益于 TypeScript 这种类型推导机制和能力,使得我们无须显式声明,即可直接通过上下文环境推断出变量的类型,也就是说此时类型可缺省。
如下所示,我们发现这些缺省类型注解的变量可以通过类型推断出类型。

{
  let string = 'it is a string';
  let number = 666;
  let boolean = false;
}
{
  const string = 'it is a string'; 
  const number = 666;
  const boolean = false;
}

我们通过 VS Code hover 示例中的变量查看类型,可以发现一个很神奇的事情。通过 let 和 const 定义的赋予了相同值的变量,其推断出来的类型不一样。比如同样是 'it is a string',通过 let 定义的变量类型是 string,而通过 const 定义的变量类型是 'it is a string''(这里表示一个字符串字面量类型)。


image.png
image.png

字面量类型
在 TypeScript 中,字面量不仅可以表示值,还可以表示类型,即所谓的字面量类型。
目前,TypeScript 支持 3 种字面量类型:字符串字面量类型、数字字面量类型、布尔字面量类型,对应的字符串字面量、数字字面量、布尔字面量分别拥有与其值一样的字面量类型,字面量类型是集合类型的子类型,它是集合类型的一种更具体的表达。比如 'it is a string' (这里表示一个字符串字面量类型)类型是 string 类型(确切地说是 string 类型的子类型),而 string 类型不一定是 'it is astring'(这里表示一个字符串字面量类型)类型。
一般来说,我们可以使用一个字符串字面量类型作为变量的类型,如下代码所示:

let userName: 'able' = 'able';
userName = 'moriaty';//不能将类型“"moriaty"”分配给类型“"able"”。ts(2322)

实际上,定义单个的字面量类型并没有太大的用处,它真正的应用场景是可以把多个字面量类型组合成一个联合类型,用来描述拥有明确成员的实用的集合。
如下代码所示,我们使用字面量联合类型描述了一个明确、可 'up' 可 'down' 的集合,这样就能清楚地知道需要的数据结构了。

type sportsType = 'basketball' | 'footbal';
function exercise(type: sportsType) {
  // ...
}
exercise('basketball'); 
exercise('run'); // 类型“"run"”的参数不能赋给类型“sportsType”的参数。ts(2345)

通过使用字面量类型组合的联合类型,我们可以限制函数的参数为指定的字面量类型集合,然后编译器会检查参数是否是指定的字面量类型集合里的成员。
因此,相较于使用 string 类型,使用字面量类型(组合的联合类型)可以将函数的参数限定为更具体的类型。这不仅提升了程序的可读性,还保证了函数的参数类型,可谓一举两得。
数字字面量类型和布尔字面量类型的使用与字符串字面量类型的使用类似,我们可以使用字面量组合的联合类型将函数的参数限定为更具体的类型。

字面量类型拓宽
所有通过 let 或 var 定义的变量、函数的形参、对象的非只读属性,如果满足指定了初始值且未显式添加类型注解的条件,那么它们推断出来的类型就是指定的初始值字面量类型拓宽后的类型,这就是字面量类型拓宽。
下面我们通过字符串字面量的示例来理解一下字面量类型拓宽:

{
  let string = 'it is a string'; // 类型是 string
  let stringFunc = (str = 'it is a string') => str; // 类型是 (str?: string) => string;
  const specifiedStr = 'it is a string'; // 类型是 'it is a string'
  let strNew = specifiedStr; // 类型是 'string'
  let stringFuncNew = (str = specifiedStr) => str; // 类型是 (str?: string) => string;
}

因为string和stringFunc满足了 let、形参且未显式声明类型注解的条件,所以变量、形参的类型拓宽为 string(形参类型确切地讲是 string | undefined)。
因为specifiedStr的常量不可变更,类型没有拓宽,所以 specifiedStr 的类型是 'it is a string' 字面量类型。
因为strNew赋予的值 specifiedStr 的类型是字面量类型,且没有显式类型注解,所以变量、形参的类型也被拓宽了。其实,这样的设计符合实际编程诉求。我们设想一下,如果 strNew 的类型被推断为 'this is string',它将不可变更,因为赋予任何其他的字符串类型的值都会提示类型错误。
但是要注意一点,如果添加显示类型注解,则能控制类型拓宽行为。

{
  const specifiedStr: ' it is a string' = 'it is a string'; // 类型是 '"it is a string"'
  let str2 = specifiedStr; // 即便使用 let 定义,类型是 'it is a string'
}
上一篇 下一篇

猜你喜欢

热点阅读