functionfn1(type: TypeEnum.A, obj: A): void functionfn1(type: TypeEnum.B, obj: B): void functionfn1(type: TypeEnum, obj: A | B) {}
//endregion
上面是一个简单的重载函数,吾辈希望在输入第一个参数 type 之后,ts 就能匹配到正确的参数,然而事实上,ts 并没能完全做到。
当然,如果真的这样写 ts 的类型检查仍然能正确地抛出错误消息,然而未能推导终究是有点问题的。
1 2 3 4 5
// TS2769: No overload matches this call. Overload 1 of 2, '(type: TypeEnum.A, obj: A): void', gave the following error. Argument of type '{ a: string; b: number; }' is not assignable to parameter of type 'A'. Object literal may only specify known properties, and 'b' does not exist in type 'A'. Overload 2 of 2, '(type: TypeEnum.B, obj: B): void', gave the following error. Argument of type 'TypeEnum.A' is not assignable to parameter of type 'TypeEnum.B' fn1(TypeEnum.A, { a: '', b: 1, })
然后,吾辈想到了几种方式可以尝试解决。
解决
继承
尝试使用继承限制字段的类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
//region 对象参数
functionfn2(arg: { type: TypeEnum.A; obj: A }): void functionfn2(arg: { type: TypeEnum.B; obj: B }): void functionfn2(arg: { type: TypeEnum; obj: A | B }) {}
fn2({ type: TypeEnum.A, obj: { a: '', }, })
//endregion
很遗憾的是,这是行不通的,即便是下面的这种变体,仍然是不可行的。
1 2 3 4 5 6 7 8 9 10 11 12
interfaceBase<T extendsTypeEnum> { type: T }
interface IA extendsBase<TypeEnum.A> { obj: A } interface IB extendsBase<TypeEnum.B> { obj: B }
functionfn2(arg: IA | IB) {}
泛型
事实上,使用泛型确实可以做到让 ts 的类型更加 正确。
缺点:
不能使用 ts 的重载
需要函数的作者改变思维
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
//region 泛型
typeEnumTypeMapGen<T extendsstring[], M extends { [P inTypeEnum]: any }> = [] typeTypeMap = { [TypeEnum.A]: A [TypeEnum.B]: B }
function fn3<T extendsTypeEnum, ArgextendsTypeMap[T]>(type: T, obj: Arg) {}