Typescript参数类型约束
场景需求:
最近在写库的时候,遇到了这么一个需求,我想限制调用者参数输入,首先有这么一个方法,接收两个参数,第二个参数根据第一参数来变化。
先看代码
class Subscription{
type:string;
topic: string;
constructor(type: string, topic: string) {
this.type = type;
this.topic = topic;
}
}
好的,首先有一个订阅类,构造方法接受type和topic两个参数。
看参数,订阅有分类型,类型又有主题
事件推送:{type:"EVENT",topic:"*"}
系统推送-ping:{type:"SYSTEM",topic:"ping"}
系统推送-disconnect:{type:"SYSTEM",topic:"disconnect:"}
回调推送-robot:{type:"CALLBACK",topic:"robot"}
回调推送-card:
需求明确:需要在type为"EVENT"的时候,topic只能输入"*",type为"SYSTEM"时,topic只能输入"ping"和"disconnect"
代码实现
先定义订阅类型
type SubscriptionType = "SYSTEM" | "EVENT" | "CALLBACK";
将订阅类型作为属性,topic作为类型,一一映射
type Topics = {
SYSTEM: "ping" | "disconnect";
EVENT: "*";
CALLBACK: "robot" | "card";
};
构造函数私有化,创建create泛型方法供外部调用
class Subscription {
readonly type: SubscriptionType;
readonly topic: string;
private constructor(type: SubscriptionType, topic: string) {
this.type = type;
this.topic = topic;
}
static create<T extends SubscriptionType>(type: T, topic: Topics[T]) {
return new Subscription(type, topic);
}
}
测试

感觉还可以,但是type的类型好像有点多余了,再进一步优化
//删除SubscriptionType
// type SubscriptionType = "SYSTEM" | "EVENT" | "CALLBACK";
type Topics = {
SYSTEM: "ping" | "disconnect";
EVENT: "*";
CALLBACK: "robot" | "card";
};
//修改type类型为string
export class Subscription {
readonly type: string;
readonly topic: string;
private constructor(type: string, topic: string) {
this.type = type;
this.topic = topic;
}
// 修改泛型方法
static create<T extends keyof Topics, K extends Topics[T]>(type: T, topic: K) {
return new Subscription(type, topic);
}
}
到这感觉更OK了,测试好用!

还没完,原理搞明白了,不想调用create方法,只想通过new的方式。
那就继续优化,直接将泛型约束作用到类上。
export class Subscription<T extends keyof Topics, K extends Topics[T]> {
readonly type;
readonly topic;
constructor(type: T, topic: K) {
this.type = type;
this.topic = topic;
}
};
依然好用

总结
主要依靠了Typescript的泛型功能,加上keyof运算符来和索引类型来实现我们想要的效果。extends关键字可以确保不会获取其他属性。
得以于Typescript强大的泛型系统,想要实现这种效果还有其他的方式,其思想是一样的,我这里就不再一一列举了。
浙公网安备 33010602011771号