# 高级类型

下面搞点稍微复杂一些的类型,可以通过类型组合的形式创建新的类型。

# 联合类型(Union Types)

通过联合的形式,使用已有的类型创建新的类型,它表示的值可以是这些类型中的任何一个,如下面的例子,page 可能是一个 string 也可能是一个 number,联合类型在使用前需要确定他的具体类型

function setPage(page: string | number) {
  let current: number;
  if (typeof page === "string") {
    // 这里的 page 就是 string 类型
    current = parseInt(page);
  } else {
    // 以为联合类型只有两个类型,所以这里也只剩下了 number 类型了
    // 这种方式就缩小了联合的范围
    current = page;
  }
  // TODO 继续用 current 来搞事情
}

如果使用联合类型的共同属性或者兼容两者的函数,那么是不需要类型断言而直接使用。例如:const a = page.toString() 或者 parseInt(page)

对于缩小联合类型的范围,下一个章节会具体讲解

# 交叉类型(Intersection Types)

交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。

interface handleProps {
  name: string;
  dataSource: any[];
}
interface handleEvent {
  onChange?: (data:any) => void;
}

export default withFetchHandler(component: handleProps & handleEvent);

在这里的 component 既有 handleProps 也有 handleEvent 的特性。

# 类型别名

前面有个例子,为了说明 object types,那么我们可以通过类型别名来定义它。

function setPoint(p: { x: number; y: number }) {
  console.log("x", p.x);
  console.log("y", p.y);
}

修改后

type IPoint = {
  x: number;
  y: number;
};
function setPoint(p: IPoint) {
  console.log("x", p.x);
  console.log("y", p.y);
}

并且上面的联合类型也可以使用别名来定义

type Ipage = string | number;

# 接口 Interfaces

接口声明是命名对象类型的另一种方法,也是比较常用的方法

interface IPoint {
  x: number;
  y: number;
}
function setPoint(p: IPoint) {
  console.log("x", p.x);
  console.log("y", p.y);
}

# 别名和接口的区别是什么?

最大的区别就是 interface 可以扩展,但是 type 不行

interface Box {
  height: number;
  width: number;
}
interface Box {
  scale: number;
}

const a: Box = { height: 10, width: 20, scale: 30 };

# 类型断言

const res = fetchSomeApi() as IRes;

也可以使用下面的方式

const res = <IRes>fetchSomeApi();

注意不要在 jsx 中使用,毕竟是 <> 组成的

类型的推断必须是可行的,例如下面的就是铁定不行的

const x = "hello" as number;

有时候碰到不能断言时,可以尝试两次断言

const a = (expr as any) as T;

# 字符串字面量类型

只能是文字类型, let a: 'hello' = 'hello' ,如果 a 的值不是 hello 的话会报错。

通常只有一个文字类型的变量没多大意义,基本上都是通过联合类型创建多个文本类型的联合类型,类似于提供了选项。

function operate(type: "add" | "update" | "delete") {
  if (type === "add") {
    // TODO 继续搞事情
  }
  // else 搞事情
}

在这里 type 的值只能是 add , update , delete 其中之一,如果出现其他的值会报错。

# 数字字面量类型

与字符串字面量类似

function rollDie(): 1 | 2 | 3 | 4 | 5 | 6 {
  // ...
}

# 字面量推断

如同前面所述,ts 可以根据字面量推断出变量的类型

const p = { url: "/account/login", method: "GET" };
// const q: {
//   url: string;
//   method: string;
// };

如果接受变量的方法是这样定义的呢?

function request(params: {
  url: string;
  method: "GET" | "POST" | "PUT" | "DELETE";
}) {
  // TODO 搞事情了
}

方法一

const p = {url: '/account/login', method: 'GET' as 'GET'};
// const p: {
//     url: string;
//     method: "GET";
// } = {url: '/account/login', method: 'GET'};

方法二

const p = { url: "/account/login", method: "GET" } as const;
// const p: {
//     readonly url: "/account/login";
//     readonly method: "GET";
// } = { url: "/account/login", method: "GET" }