TypeScript的入门与放弃(3)之 高级类型

379 阅读6分钟
  • 在学习中,看到了其他的知识点 字面量 以及字面量类型,联合类型,交叉类型等等,接下来,就来学习下吧。

1. 字面量类型

在 TypeScript 中,字面量不仅可以表示值,还可以表示类型,即所谓的字面量类型。TypeScript 支持 3 种字面量类型:字符串字面量类型、数字字面量类型、布尔字面量类型。对应的字符串字面量、数字字面量、布尔字面量分别拥有与其值一样的字面量类型

let str: 'hello world' = 'hello world'; 
let num: 123 = 123;
let bool: true = true
//代表着 str 的类型就是 ‘hello world' 

1.1 字符串字面量

type Name = "ms";
const name1: Name = "test"; // error 不能将类型"test"分配给类型"ms"
const name2: Name = "ms";
//类型为 ‘ms’字面量类型,只能赋值‘ms’
let Name: 'ms' = 'ms'; 
let realName: string = 'realName'; 
Name = realName; //不能将类型“string”分配给类型“"ms"”。ts(2322)
realName = Name; // ok

这里的Name是‘ms’类型 是 string类型的子类型;那父类型不能赋值给子类型,但是子类型可以赋值给父类型。换句话说,‘ms’类型一定是string类型,但是string类型不全是‘ms’类型。

有单个,也可以有多个类型合起来使用。

type Lang = "js" | "ts" | "java" | "c";
function getLang(lang: Lang) {
  return lang
}
getLang("test"); // error 类型"test"的参数不能赋给类型“Lang”的参数
getLang("js"); //ok
getLang("java");//ok

1.2 数字字面量类型及布尔字面量类型

三者大多用法相似,都是指定类型为具体的值

interface Config { 
    size: 'small' | 'big'; 
    isEnable: true | false;
    margin: 0 | 2 | 4; 
}
const info: Config = { 
    size: "big", 
    isEnable:false,
    margin: 28 //不能将类型“28”分配给类型“0 | 2 | 4”。ts(2322)
};

1.3 let与const的不同分析

在学习中,看到了对let 与 const的不同分析。

const str = 'ms'; 
const num = 1; 
const bool = true; 

const声明常量,不可修改,在缺省类型注解的情况下,TypeScript 推断出它的类型直接由赋值字面量的类型决定。

let str = "ms";
let num = 123;
let bool = false;

这样以 let 声明的话,比如str的类型是字符串字面量类型"ms"的父类型string,num的类型是数字字面量类型123的父类型number,bool的类型是布尔字面量类型false的父类型boolean 按照之前父类型不能赋值给子类型,但是子类型可以赋值给父类型 可以进行赋值。

str = "hello ms";
num = 666;
bool = true;

我们将 TypeScript 的字面量子类型转换为父类型的这种设计称之为 "literal widening",也就是字面量类型的拓宽.接下来,就来看看字面量类型的拓宽的详细内容。

1.4 类型拓宽

通过 let 或 var 定义的变量、函数形参、对象的非只读属性,如果指定了初始值且未显式添加类型注解,那么它们推断出来的类型就是指定的初始值字面量类型拓宽后的类型,这就是字面量类型拓宽

  let str = 'ms'; // 类型是 string
  let strFun = (str = 'ms') => str; // 类型是 (str?: string) => string;
  const specifiedStr = 'ms'; // 类型是 'ms'
  let str2 = specifiedStr; // 类型是 'string'
  let strFun2 = (str = specifiedStr) => str; // 类型是 (str?: string) => string;

第一段代码中通过let定义了字符串str,是一个形参,并且没有显式的声明其类型,属于是类型拓宽,所以变量和形参推断出类型为string。

第二段代码中通过const定义了字符串specifiedStr,这个字符串是常量,不能进行修改,所以specifiedStr的类型为ms字面量类型,后面的str2遍历和strFun2函数形参被赋值了字面量类型的常量,并且没有显式的声明其类型,所以变量、形参的类型都被拓宽了,并没有被指定为它对应的字面量类型。

2. 联合类型

如果希望属性为多种类型之一,如字符串或者数组,这时联合类型就派上用场了(它使用 | 作为标记,如 string | number)。联合类型可以理解为多个类型的并集联合类型用来表示变量、参数的类型不是某个单一的类型,而可能是多种不同的类型的组合

function formatCommandline(command: string[] | string) {
  let line = '';
  if (typeof command === 'string') {
    line = command.trim();
  } else {
    line = command.join(' ').trim();
  }
}
//也可以使用类型别名抽离联合类型
type Command = string[] | string
function formatCommandline(command:Command) {
  let line = '';
  if (typeof command === 'string') {
    line = command.trim();
  } else {
    line = command.join(' ').trim();
  }
}

联合类型表示一个值可以是几种类型之一,用竖线 | 分隔每个类型,所以 number | string | boolean 表示一个值可以是number、string、boolean类型中的任意一种

之前我们提到过字面量类型那么如果一个‘ms’字符串字面量类型和string类型 联合类型会发生呢干什么呢?

2.1 类型缩减

type UnionNum = 1 | number;  // 类型是number
type UniomStr = "string" | string;  // 类型是string
type UnionBool = false | boolean;   // 类型是boolean

但是会遇到问题TypeScript会对类型进行缩减,将字面量类型去掉,保留原始类型 编译器只能提示我们定义的变量是那个原始的类型

type UniomStr = "string" | string;  // 类型是string
let str : UniomStr = 'string'//类型是string 

//TypeScript提供了一种方式来控制类型缩减,只需给父类型添加"& {}"即可

type UniomStr = "string" | string & {};  // 类型是string
let str : UniomStr = 'string'//类型是UniomStr

3. 交叉类型

交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到成为一种类型,合并后的类型将拥有所有成员类型的特性。交叉类型可以理解为多个类型的交集。

//使用“&”操作符来声明交叉类型
type Overlapping = string & number; //类型显示为never

将多个接口类型合并成为一个类型是交叉类型的一个常见的使用场景。这样就能相当于实现了接口的继承,也就是所谓的合并接口类型

type IntersectionType = { id: number; name: string; } & { age: number }; 
const mixed: IntersectionType = { 
    id: 1, 
    name: 'name', 
    age: 18
}

万一,都有age而且类型不同,会发生什么呢

type IntersectionType = { 
    id: number; name: string;age:string 
} & { age: number }; 
const mixed: IntersectionType = { 
    id: 1, 
    name: 'name', 
    age: 18//不能将类型“number”分配给类型“never”。
}

age 被合并成 string & number 变成了never类型。

交叉类型另外一个常见的使用场景就是合并联合类型。可以合并多个联合类型为一个交叉类型,这个交叉类型需要同时满足不同的联合类型限制,也就是提取了所有联合类型的相同类型成员

type A = "blue" | "red" | 123;
type B = 123 | 666;
type C = A & B;//类型为123

//万一没有交集
type A = "blue" | "red" ;
type B = 123 | 666;
type C = A & B;//类型为never

打完喝水,继续加油!!