前言
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" // ×