前言
ts如果联合类型中2个子类型是相似(甚至完全一致),则会有ts类型合并的情况。我们来看一段示例代码:
type ListToolsRequest = {
id?: string;
}
type ListDiagnosisRequest = {
id?: string;
}
type CommonRes<T> = {
code?: string;
data?: T;
msg?: string;
}
type ListToolsDataRes = Record<string, string>[];
type ListDiagnosisDataRes = Record<string, string>[];
type ListTools = (...args:ListToolsRequest []) => Promise<CommonRes<ListToolsDataRes>>;
type ListDiagnosis = (...args: ListDiagnosisRequest[]) => Promise<CommonRes<ListDiagnosisDataRes>>;
const listTools: ListTools = (...args) => {
console.log(1111, args);
return new Promise((resolve) => resolve({ code:'200', data:[], msg:'测试1'}))
}
const listDiagnosis: ListDiagnosis = (...args) => {
console.log(1111, args);
return new Promise((resolve) => resolve({ code:'200', data:[], msg:'测试2'}))
}
let method: ListDiagnosis | ListTools;
enum MethodType {
DIAGNOSIS = 'diagnosis',
Tool = 'tool'
}
const fn = (type: MethodType) => {
if(type === MethodType.DIAGNOSIS){
method = listDiagnosis;
}
if(type === MethodType.Tool){
method = listTools;
}
if(method){
method().then(res => console.log(res));
}
}
解读
这段类型代码,我们模拟了2个接口函数类型,分别是ListDiagnosis和ListTools。首先我们定义了ListToolsRequest类型,这个类型为ListTools的参数类型,参数有一个id(当然如果是真实的接口,参数值很少有只有一个的情况,这里只是模拟,只需要一个属性即可),同理ListDiagnosisRequest类型也是。其实我们定义了一个共同的返回值类型,即CommonRes类型,这是一个泛型,泛型T代表的是返回的列表数据类型。随后我们又定义了2个接口函数的列表返回值类型,即ListToolsDataRes和ListDiagnosisDataRes,它们是一个对象数组。随后我们模拟创建了listTools和listDiagnosis,它们2个函数以及对应的类型。
然后,我们定义了method变量,它是一个联合类型,很显然它的值不是listDiagnosis就是listTools。
然后我们定义了一个枚举值,枚举值包含了2个值,最后我们定义了一个函数,函数有一个参数,参数类型就是这个枚举值,然后我们根据传入的type值,来确定调用的是哪个接口函数,最后,我们判断method是否存在值,存在则调用,并得到接口的返回值。
然后请问fn中的method变量的类型应该是怎么样的?
我想大多数人应该跟我一样的想法,即它的类型是ListDiagnosis | ListTools,也就是说它应该是一个联合类型。
当然,这个答案理论上是没有问题的,但是我们忽略了ts的一个特性,那就是类型合并,通过观察,我们其实可以发现ListDiagnosis和ListTools2个类型是相似的(甚至可以说是完全相同),然后ts编译器会将2个子类型进行合并,最终结果应该是ListDiagnosis或者是ListTools(非联合类型)其中的一个。如下图所示:
示例代码,可以参考这里。
所以如果有读者遇到跟我一样的情况,不必讶异,这就是预期的情况。