首先先来描述一个需求场景,例如有一个创建用户和更新用户的功能,其中要传给服务端的字段除了「更新」需要多一个「id」属性以外其余属性都一样。
在此我先定义一个接口来声明这个参数对象的类型:
interface Prop{
action:'create'|'update';
id?:string;
name:string;
}
但是我们发现,这个接口并不满足我们的需求,因为我们这样创建对象依然会通过:
const a:Prop = {
action:'create',
id:'1',
name:'zhangsan'
}
这个类型并不会通过 action 的值来帮我判断是否应该有 id 字段。
接下来换个写法来改进一下:
type Prop =
| {
action: "create";
name: string;
}
| {
action: "update";
id: string;
name: string;
};
const a: Prop = {
action: "update",
name: "zhangsan"
};
这时如果我们声明一个 Prop 类型的变量没有 id 属性,TypeScript 就会报出异常:
Property 'id' is missing in type '{ action: "update"; name: string; }' but required in type '{ action: "update"; id: string; name: string; }'.
这就顺利解决了我们的问题,但是接着又遇到一个问题,当我在一个函数中传入 Prop 类型的参数,并使用了它的 id 属性时,TypeScript 又报了一个错误:
type Prop =
| {
action: "create";
name: string;
}
| {
action: "update";
id: string;
name: string;
};
function fn(e: Prop) {
console.log(e.id);
}
// Property 'id' does not exist on type '{ action: "create"; name: string; }'.
原来是 TypeScript 并不确定你传进来的对象的 action 为 update 还是 create,所以继续改进一些代码来帮助 TypeScript 判断:
type Prop =
| {
action: "create";
name: string;
}
| {
action: "update";
id: string;
name: string;
};
function fn(e: Prop) {
if (e.action === "create") {
console.log(e.name);
} else {
console.log(e.id);
}
}
这样报错就顺利解决了。
我们一直在使用的 type Prop 这个类型就是一个可辨识联合,从上面的代码来看它有以下特点:
- 有一个共有的字段。在上文中这个共有字段是
action。 - 这个共有字段的值是可穷举的。因为如果值是不可穷举的,就无法用选择语句来对变量的具体类型进行判断。