一般来说我们可以使用联合类型来代表几种类型,如下:

1
const value: string | number;

字符串字面量类型也是一种联合类型。一个字符串字面量只能被赋值给特定的字符串值。可以作为一个字符串类型的子类型。

1
type UserRole = "admin" | "moderator" | "author";

对于下面这个简单的接口:

1
2
3
4
5
6
interface User {
  id: number;
  name: string;
  email: string;
  role: UserRole;
}

使用 keyof 就可以得到一个字符串字面量联合类型:

1
type UserKeysType = keyof User; // 'id' | 'name' | 'email' | 'role'

使用对象属性获取值的类似方式可以获取到类型:

1
type IdType = User["id"]; // number

索引类型( index type )就是类似上面的方式,通过索引得到的值的类型,可以通过几个例子来感受一下:

获取对象属性

我们可以使用更动态的方式来获取到值的类型,比如以下这个例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const user: User = {
  id: 15,
  name: "John",
  email: "john@smith.com",
  role: "admin",
};

function getProperty<T, K extends keyof T>(object: T, prop: K): T[K] {
  return object[prop];
}

getProperty(user, "id"); // 15
getProperty(user, "id").toLowerCase(); // 类型“number”上不存在属性“toLowerCase”。

extends 可以用来继承一个类,也可以用来继承一个接口。此时 K 就类似上面提到的 UserKeysType。所以T[K] 指的就是 User[UserKeysType] 的类型。

对象生成 Map

通常我们会用 Object.entries 来对对象进行操作生成 Map:

1
2
3
4
5
6
7
const settings = {
  isModalOpened: true,
  canDelete: false,
  role: "Admin",
};

const settingsMap = new Map(Object.entries(settings));

上面的代码虽然是合理的,但是无法验证更具体的类型:

1
settingsMap.get("role").toLowerCase(); // 类型“string | boolean”上不存在属性“toLowerCase”。类型“false”上不存在属性“toLowerCase”。

上面的错误表示 settingsMap.get 返回的是 string | boolean 的联合类型,怎么让他知道 role 属性对应的是 string 类型呢,可以创建一个从 Map 继承来的类型并且提供更具体的类型:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
interface MapFromObject<T, K extends keyof T> extends Map<K, T[K]> {
  get: <U extends keyof T>(key: U) => T[U];
}

const settingsMap = new Map(Object.entries(settings)) as MapFromObject<
  Settings,
  keyof Settings
>;

settingsMap.get("role").toLowerCase(); // 'admin'

还有一些很多用处的内置修饰符,比如 PickPartial

1
2
3
4
5
6
7
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

type Partial<T> = {
  [P in keyof T]?: T[P];
};

还有条件类型:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type WithNumbersInsteadOfStrings<T> = {
  [K in keyof T]: T[K] extends number ? string : T[K];
};

const user: WithNumbersInsteadOfStrings<User> = {
  id: "15", // id也变为了string类型
  name: "John",
  email: "john@smith.com",
  role: "admin",
};