我们给这一段程序添加ts
const personWatcher = watchTest({
firstName: "Saoirse",
lastName: "Ronan",
age: 20,
});
personWatcher.on("lastNameChange", (oldValue, newValue) => {});
正常我们添加的ts是这样的 但是这样添加上的ts基本没什么效果
type Watcher = {
on(name: string, cb: (oldValue: any, newValue: any) => void): void;
};
interface IWatchTest {
firstName: string;
lastName: string;
age: number;
}
declare function watchTest(obj: IWatchTest): Watcher;
const personWatcher = watchTest({
firstName: "Saoirse",
lastName: "Ronan",
age: 20,
});
personWatcher.on("lastNameChange", (oldValue, newValue) => {});
现在我们有个需求
- 将on后面监听的属性change方法要和传入进来的对象中的键绑定
- 也就是说我们传入的对象键值为 firstName|lastName|age 我们对应监听的change只能是
- firstNameChange | lastNameChange | ageChange 如果是xxxChange就会给出报错提示
- 我们该如何实现呢 思路就是使用联合类型 将上面on里面的name不要只限制为string要让他是传入对象键的联合类型
改造如下:
其中string & keyof T 意思是只取对象键中是字符串的键 过滤掉Symbol等
type Watcher<T> = {
on(
name: `${string & keyof T}Change`,
cb: (oldValue: any, newValue: any) => void
): void;
};
interface IWatchTest {
firstName: string;
lastName: string;
age: number;
}
declare function watchTest<T>(obj: T): Watcher<T>;
const personWatcher = watchTest<IWatchTest>({
firstName: "Saoirse",
lastName: "Ronan",
age: 20,
});
personWatcher.on("aaaChange", (oldValue, newValue) => {});
aaaChange会报错ageChange编译通过
现在我们已经实现了限制监听方法名字, 现在我又来了一个需求我希望返回回来的oldValue和newValue和我 监听的属性类型是一样的我该怎么办呢
- 首先 如果要拿到对象的类型我们可以通过T[K]的方式来拿到
- 我们就可以多定义一个K的泛型 主要限制传入的是对象的键
改造如下:
K extends string & keyof T K必须限制是T中的某个键不能是其他
type Watcher<T> = {
on<K extends string & keyof T>(
name: `${string & keyof T}Change`,
cb: (oldValue: T[K], newValue: T[K]) => void
): void;
};
interface IWatchTest {
firstName: string;
lastName: string;
age: number;
}
declare function watchTest<T>(obj: T): Watcher<T>;
const personWatcher = watchTest<IWatchTest>({
firstName: "Saoirse",
lastName: "Ronan",
age: 20,
});
personWatcher.on<"age">("ageChange", (oldValue, newValue) => {});
这样看起来就舒服多了 也能很方便的拿到返回的类型文章来源:https://uudwc.com/A/dMvmx
接下来我想在做一层限制就是我想让我的泛型K和on绑定的属性名称也关联上,因为我现在的代码如果这样写
personWatcher.on<"lastName">("ageChange", (oldValue, newValue) => {});
他是不会提示你报错的文章来源地址https://uudwc.com/A/dMvmx
那么我们该如何关联和 很简单把name后面定义为K就可以了
完整版
type Watcher<T> = {
on<K extends string & keyof T>(
name: `${K}Change`,
cb: (oldValue: T[K], newValue: T[K]) => void
): void;
};
interface IWatchTest {
firstName: string;
lastName: string;
age: number;
}
declare function watchTest<T>(obj: T): Watcher<T>;
const personWatcher = watchTest<IWatchTest>({
firstName: "Saoirse",
lastName: "Ronan",
age: 20,
});
personWatcher.on<"lastName">("lastNameChange", (oldValue, newValue) => {});