前言
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
const a = "1";const b: typeof a = "1";
const a: "1" = "1"字面量约束,b 的类型和 a 保持一致,b 也是字面量类型,等效于 const b: "1"。
⚠️ 不要误以为 const a = "1"这么写,类型推断的结果是 a: string,正确的推断结果是 a: "1",表示 a 被约束为一个字面量类型,只能是 "1"。
const a: string = "1";const b: typeof a = "2";
a: string已经指定 a 的类型是 string 类型,const b: typeof a等效于 const b: string
typeof 可以写在类型约束的位置上,表示获取某个数据的类型。
class User {loginId: string = "admin";loginPwd: string = "123123";}function createUser(cls: new () => User): User {return new cls();}const u = createUser(User);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
interface User {loginid: string;loginpwd: string;age: number;}const u: User = {loginid: "admin",loginpwd: "123123",age: 23,};function printUserProperty(obj: User, propName: string) {console.log(obj[propName]); // 报错}printUserProperty(u, "age");
obj[propName]会报错,因为无法确保 propName 一定存在。
🤔 **propName: string** 这个如何修改,才能避免报错?
答:只要确保 propName 只可能是 obj 中存在的 key 即可。
👇🏻 两种写法都行,它们是等效的。
propName: "loginid" | "loginpwd" | "age"propName: keyof User
keyof 作用于类、接口、类型别名,用于获取其它类型中的所有成员名组成的联合类型。
in
interface User {loginid: string;loginpwd: string;age: number;}type Obj = {[prop: string]: string};const o: Obj = {};o.a = "1"; // √
[prop: string]: string
变量 o 的类型由类型别名 Obj来约束,Obj中的索引器表示,只要新增的属性和属性值满足要求:key、value 都是字符串类型,那么就可以随意新增。
现在有这么一个需求
- Obj 中的 key 只能取自 User 接口中的 key
- key 对应的 value 类型也要和 User 接口对应
interface User {loginid: string;loginpwd: string;age: number;}type Obj = {[prop: "loginid" | "loginpwd" | "age"]: string;};

interface User {loginid: string;loginpwd: string;age: number;}type Obj = {[prop in "loginid" | "loginpwd" | "age"]: string;// 等效于// loginid: string;// loginpwd: string;// age: string;};
这么做,其实就已经实现了第一个需求:“Obj 中的 key 只能取自 User 接口中的 key”。
存在的隐患:prop 的取值是写死的,而 User 中的 key 随时可能会发生变化。
interface User {loginid: string;loginpwd: string;age: number;}type Obj = {[prop in keyof User]: User[prop];};

Obj 类型和 User 类型约束一致。
interface User {loginid: string;loginpwd: string;age: number;}interface Article {title: string;content: string;}// 将所有的属性值约束为 string 类型type valToString<T> = {[p in keyof T]: string;};// 将所有属性都变为可选的type beOptional<T> = {[p in keyof T]?: T[p];};// 将所有属性变为只读的type beReadonly<T> = {readonly [p in keyof T]: T[p];};const u: valToString<User> = {loginid: "",loginpwd: "",age: ""}const u2: beOptional<User> = {};const a: beReadonly<Article> = {title: "",content: ""}// a.title = "123" // ×
