TypeScript函数参数类型推断问题:转Ref后仍类型检查报错
TypeScript类型检查问题:转换参数为Ref后仍报联合类型错误
问题代码
export default function (triggerEl: Ref<HTMLDivElement>, contentEl: Ref<HTMLDivElement>, placement: Ref<Placement> | Placement = 'auto', offset: Ref<number> | number = 0) { placement = ref(placement) as Ref<Placement> offset = ref(offset) const { top: triggerTop, right: triggerRight, bottom: triggerBottom, left: triggerLeft, width: triggerWidth, height: triggerHeight } = useElementBounding(triggerEl) const { top: contentTop, right: contentRight, bottom: contentBottom, left: contentLeft, width: contentWidth, height: contentHeight } = useElementBounding(contentEl) const position = computed<PopperPosition>(() => { let top = Number.MIN_SAFE_INTEGER let left = Number.MIN_SAFE_INTEGER if (placement.value === 'top') { top = triggerTop.value - contentHeight.value - offset.value left = triggerLeft.value + triggerWidth.value / 2 - contentWidth.value / 2 } return { top, left, } }) return null }
问题描述
已将函数参数placement和offset转换为对应的Ref类型,但后续代码仍认为它们是Ref<Placement> | Placement、Ref<number> | number的联合类型,导致类型检查报错,而实际上它们不会再是原始类型,该如何解决?
解决方案
方法一:重新声明变量(推荐)
函数参数的类型注解是固定的,直接赋值不会改变参数的类型。可以重新声明新变量,明确其Ref类型,彻底避免类型歧义:
export default function (triggerEl: Ref<HTMLDivElement>, contentEl: Ref<HTMLDivElement>, placement: Ref<Placement> | Placement = 'auto', offset: Ref<number> | number = 0) { // 重新声明变量,覆盖原参数的联合类型 const placementRef = ref(placement) as Ref<Placement> const offsetRef = ref(offset) as Ref<number> const { top: triggerTop, right: triggerRight, bottom: triggerBottom, left: triggerLeft, width: triggerWidth, height: triggerHeight } = useElementBounding(triggerEl) const { top: contentTop, right: contentRight, bottom: contentBottom, left: contentLeft, width: contentWidth, height: contentHeight } = useElementBounding(contentEl) const position = computed<PopperPosition>(() => { let top = Number.MIN_SAFE_INTEGER let left = Number.MIN_SAFE_INTEGER if (placementRef.value === 'top') { top = triggerTop.value - contentHeight.value - offsetRef.value left = triggerLeft.value + triggerWidth.value / 2 - contentWidth.value / 2 } return { top, left, } }) return null }
方法二:使用类型断言缩小参数类型
如果不想新增变量,可以在使用参数前通过类型断言明确其类型,让TypeScript识别到实际类型:
export default function (triggerEl: Ref<HTMLDivElement>, contentEl: Ref<HTMLDivElement>, placement: Ref<Placement> | Placement = 'auto', offset: Ref<number> | number = 0) { placement = ref(placement) as Ref<Placement> offset = ref(offset) as Ref<number> // 显式缩小参数类型 const _placement = placement as Ref<Placement> const _offset = offset as Ref<number> const { top: triggerTop, right: triggerRight, bottom: triggerBottom, left: triggerLeft, width: triggerWidth, height: triggerHeight } = useElementBounding(triggerEl) const { top: contentTop, right: contentRight, bottom: contentBottom, left: contentLeft, width: contentWidth, height: contentHeight } = useElementBounding(contentEl) const position = computed<PopperPosition>(() => { let top = Number.MIN_SAFE_INTEGER let left = Number.MIN_SAFE_INTEGER if (_placement.value === 'top') { top = triggerTop.value - contentHeight.value - _offset.value left = triggerLeft.value + triggerWidth.value / 2 - contentWidth.value / 2 } return { top, left, } }) return null }
方法三:利用函数重载明确类型映射
如果需要对外支持多种调用方式(传入原始值或Ref),可以通过函数重载声明,让TypeScript自动推导内部处理后的类型:
// 重载声明:定义两种合法的调用签名 export default function usePopper(triggerEl: Ref<HTMLDivElement>, contentEl: Ref<HTMLDivElement>, placement: Placement, offset?: number): null export default function usePopper(triggerEl: Ref<HTMLDivElement>, contentEl: Ref<HTMLDivElement>, placement: Ref<Placement>, offset?: Ref<number>): null // 实现函数:处理联合类型参数 export default function usePopper(triggerEl: Ref<HTMLDivElement>, contentEl: Ref<HTMLDivElement>, placement: Ref<Placement> | Placement = 'auto', offset: Ref<number> | number = 0) { placement = ref(placement) as Ref<Placement> offset = ref(offset) as Ref<number> const { top: triggerTop, right: triggerRight, bottom: triggerBottom, left: triggerLeft, width: triggerWidth, height: triggerHeight } = useElementBounding(triggerEl) const { top: contentTop, right: contentRight, bottom: contentBottom, left: contentLeft, width: contentWidth, height: contentHeight } = useElementBounding(contentEl) const position = computed<PopperPosition>(() => { let top = Number.MIN_SAFE_INTEGER let left = Number.MIN_SAFE_INTEGER if (placement.value === 'top') { top = triggerTop.value - contentHeight.value - offset.value left = triggerLeft.value + triggerWidth.value / 2 - contentWidth.value / 2 } return { top, left, } }) return null }
原因说明
TypeScript中函数参数的类型注解是静态的,即使你给参数赋值了更具体的类型值,参数的类型仍会保持最初声明的联合类型。因此需要通过重新声明变量、类型断言或函数重载的方式,让TypeScript识别变量的实际类型。
内容的提问来源于stack exchange,提问作者Fan Gao
相关产品推荐
相关产品推荐

