前言

关键字
typeof、keyof、in

notes

typeof
typeof 这个关键字在 js 中也存在,但是 ts 中的 typeof 和 js 中的 ts 有所不同。
在 ts 中,对 typeof 关键字的功能进行了拓展,如果写法和 js 中一致,那么功能和 js 中的 typeof 是一样的,下面介绍它在 ts 中的新用法。

  • 可以写在类型约束的位置上,表示获取某个数据的类型。
  • 如果作用于类,获取到的类型是该类的构造函数。

keyof
作用于类、接口、类型别名,用于获取其它类型中的所有成员名组成的联合类型。

in
in 关键字通常会和 keyof 关键字联用,用于限制某个索引类型的取值范围。

这里说的索引类型,就是指索引器前边的key 索引器:[prop: string]: string 索引类型:[prop: string]

codes

typeof

  1. const a = "1";
  2. const b: typeof a = "1";

const a: "1" = "1"字面量约束,b 的类型和 a 保持一致,b 也是字面量类型,等效于 const b: "1"

⚠️ 不要误以为 const a = "1"这么写,类型推断的结果是 a: string,正确的推断结果是 a: "1",表示 a 被约束为一个字面量类型,只能是 "1"

  1. const a: string = "1";
  2. const b: typeof a = "2";

a: string已经指定 a 的类型是 string 类型,const b: typeof a等效于 const b: string

typeof 可以写在类型约束的位置上,表示获取某个数据的类型。

  1. class User {
  2. loginId: string = "admin";
  3. loginPwd: string = "123123";
  4. }
  5. function createUser(cls: new () => User): User {
  6. return new cls();
  7. }
  8. const u = createUser(User);
  9. console.log(u.loginId, u.loginPwd); // => admin 123123

🤔 **cls: new () => User**是否还有其它等效写法?
答:**cls: typeof User****~~cls: User~~**

⚠️ 不能写 cls: User,因为 User 约束的类型是 User 实例,并不是 User 构造函数。如果使用 cls: User来约束的话,那么在调用的时候需要这么传递参数:createUser(new User())

如果 typeof 作用于类,获取到的类型是该类的构造函数。

keyof

  1. interface User {
  2. loginid: string;
  3. loginpwd: string;
  4. age: number;
  5. }
  6. const u: User = {
  7. loginid: "admin",
  8. loginpwd: "123123",
  9. age: 23,
  10. };
  11. function printUserProperty(obj: User, propName: string) {
  12. console.log(obj[propName]); // 报错
  13. }
  14. printUserProperty(u, "age");

obj[propName]会报错,因为无法确保 propName 一定存在。

🤔 **propName: string** 这个如何修改,才能避免报错?
答:只要确保 propName 只可能是 obj 中存在的 key 即可。
👇🏻 两种写法都行,它们是等效的。

  1. propName: "loginid" | "loginpwd" | "age"
  2. propName: keyof User

keyof 作用于类、接口、类型别名,用于获取其它类型中的所有成员名组成的联合类型。

in

  1. interface User {
  2. loginid: string;
  3. loginpwd: string;
  4. age: number;
  5. }
  6. type Obj = {
  7. [prop: string]: string
  8. };
  9. const o: Obj = {};
  10. o.a = "1"; // √

[prop: string]: string
变量 o 的类型由类型别名 Obj来约束,Obj中的索引器表示,只要新增的属性和属性值满足要求:key、value 都是字符串类型,那么就可以随意新增。

现在有这么一个需求

  1. Obj 中的 key 只能取自 User 接口中的 key
  2. key 对应的 value 类型也要和 User 接口对应
  1. interface User {
  2. loginid: string;
  3. loginpwd: string;
  4. age: number;
  5. }
  6. type Obj = {
  7. [prop: "loginid" | "loginpwd" | "age"]: string;
  8. };

image.png

  1. interface User {
  2. loginid: string;
  3. loginpwd: string;
  4. age: number;
  5. }
  6. type Obj = {
  7. [prop in "loginid" | "loginpwd" | "age"]: string;
  8. // 等效于
  9. // loginid: string;
  10. // loginpwd: string;
  11. // age: string;
  12. };

这么做,其实就已经实现了第一个需求:“Obj 中的 key 只能取自 User 接口中的 key”。
存在的隐患:prop 的取值是写死的,而 User 中的 key 随时可能会发生变化。

  1. interface User {
  2. loginid: string;
  3. loginpwd: string;
  4. age: number;
  5. }
  6. type Obj = {
  7. [prop in keyof User]: User[prop];
  8. };

image.png

Obj 类型和 User 类型约束一致。

  1. interface User {
  2. loginid: string;
  3. loginpwd: string;
  4. age: number;
  5. }
  6. interface Article {
  7. title: string;
  8. content: string;
  9. }
  10. // 将所有的属性值约束为 string 类型
  11. type valToString<T> = {
  12. [p in keyof T]: string;
  13. };
  14. // 将所有属性都变为可选的
  15. type beOptional<T> = {
  16. [p in keyof T]?: T[p];
  17. };
  18. // 将所有属性变为只读的
  19. type beReadonly<T> = {
  20. readonly [p in keyof T]: T[p];
  21. };
  22. const u: valToString<User> = {
  23. loginid: "",
  24. loginpwd: "",
  25. age: ""
  26. }
  27. const u2: beOptional<User> = {};
  28. const a: beReadonly<Article> = {
  29. title: "",
  30. content: ""
  31. }
  32. // a.title = "123" // ×