Skip to content

Commit 7d0c63f

Browse files
committed
fix(custom-elements): use strict number casting
close#4946close#2598close#2604 This commit also refactors internal usage of previous loose implementation of `toNumber` to the stricter version where applicable. Use of `looseToNumber` is preserved for `v-model.number` modifier to ensure backwards compatibility and consistency with Vue 2 behavior.
1 parent efa2ac5 commit 7d0c63f

File tree

8 files changed

+51
-29
lines changed

8 files changed

+51
-29
lines changed

‎packages/runtime-core/src/compat/instance.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import{
22
extend,
33
looseEqual,
44
looseIndexOf,
5+
looseToNumber,
56
NOOP,
6-
toDisplayString,
7-
toNumber
7+
toDisplayString
88
}from'@vue/shared'
99
import{
1010
ComponentPublicInstance,
@@ -148,7 +148,7 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap){
148148
$createElement: ()=>compatH,
149149
_c: ()=>compatH,
150150
_o: ()=>legacyMarkOnce,
151-
_n: ()=>toNumber,
151+
_n: ()=>looseToNumber,
152152
_s: ()=>toDisplayString,
153153
_l: ()=>renderList,
154154
_t: i=>legacyRenderSlot.bind(null,i),

‎packages/runtime-core/src/componentEmits.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import{
1010
isObject,
1111
isString,
1212
isOn,
13-
toNumber,
14-
UnionToIntersection
13+
UnionToIntersection,
14+
looseToNumber
1515
}from'@vue/shared'
1616
import{
1717
ComponentInternalInstance,
@@ -126,7 +126,7 @@ export function emit(
126126
args=rawArgs.map(a=>(isString(a) ? a.trim() : a))
127127
}
128128
if(number){
129-
args=rawArgs.map(toNumber)
129+
args=rawArgs.map(looseToNumber)
130130
}
131131
}
132132

‎packages/runtime-core/src/components/Suspense.ts‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ import{
2222
}from'../renderer'
2323
import{queuePostFlushCb}from'../scheduler'
2424
import{filterSingleRoot,updateHOCHostEl}from'../componentRenderUtils'
25-
import{pushWarningContext,popWarningContext,warn}from'../warning'
25+
import{
26+
pushWarningContext,
27+
popWarningContext,
28+
warn,
29+
assertNumber
30+
}from'../warning'
2631
import{handleError,ErrorCodes}from'../errorHandling'
2732

2833
exportinterfaceSuspenseProps{
@@ -419,6 +424,10 @@ function createSuspenseBoundary(
419424
}=rendererInternals
420425

421426
consttimeout=toNumber(vnode.props&&vnode.props.timeout)
427+
if(__DEV__){
428+
assertNumber(timeout,`Suspense timeout`)
429+
}
430+
422431
constsuspense: SuspenseBoundary={
423432
vnode,
424433
parent,

‎packages/runtime-core/src/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export{useSSRContext, ssrContextKey } from './helpers/useSsrContext'
104104

105105
export{createRenderer,createHydrationRenderer}from'./renderer'
106106
export{queuePostFlushCb}from'./scheduler'
107-
export{warn}from'./warning'
107+
export{warn,assertNumber}from'./warning'
108108
export{
109109
handleError,
110110
callWithErrorHandling,

‎packages/runtime-core/src/warning.ts‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,15 @@ function formatProp(key: string, value: unknown, raw?: boolean): any{
162162
returnraw ? value : [`${key}=`,value]
163163
}
164164
}
165+
166+
/**
167+
* @internal
168+
*/
169+
exportfunctionassertNumber(val: unknown,type: string){
170+
if(!__DEV__)return
171+
if(typeofval!=='number'){
172+
warn(`${type} is not a valid number - `+`got ${JSON.stringify(val)}.`)
173+
}elseif(isNaN(val)){
174+
warn(`${type} is NaN - `+'the duration expression might be incorrect.')
175+
}
176+
}

‎packages/runtime-dom/src/components/Transition.ts‎

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import{
22
BaseTransition,
33
BaseTransitionProps,
44
h,
5-
warn,
5+
assertNumber,
66
FunctionalComponent,
77
compatUtils,
88
DeprecationTypes
@@ -283,24 +283,10 @@ function normalizeDuration(
283283

284284
functionNumberOf(val: unknown): number{
285285
constres=toNumber(val)
286-
if(__DEV__)validateDuration(res)
286+
if(__DEV__)assertNumber(res,'<transition> explicit duration')
287287
returnres
288288
}
289289

290-
functionvalidateDuration(val: unknown){
291-
if(typeofval!=='number'){
292-
warn(
293-
`<transition> explicit duration is not a valid number - `+
294-
`got ${JSON.stringify(val)}.`
295-
)
296-
}elseif(isNaN(val)){
297-
warn(
298-
`<transition> explicit duration is NaN - `+
299-
'the duration expression might be incorrect.'
300-
)
301-
}
302-
}
303-
304290
exportfunctionaddTransitionClass(el: Element,cls: string){
305291
cls.split(/\s+/).forEach(c=>c&&el.classList.add(c))
306292
;(

‎packages/runtime-dom/src/directives/vModel.ts‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import{
1111
looseEqual,
1212
looseIndexOf,
1313
invokeArrayFns,
14-
toNumber,
14+
looseToNumber,
1515
isSet
1616
}from'@vue/shared'
1717

@@ -54,7 +54,7 @@ export const vModelText: ModelDirective<
5454
domValue=domValue.trim()
5555
}
5656
if(castToNumber){
57-
domValue=toNumber(domValue)
57+
domValue=looseToNumber(domValue)
5858
}
5959
el._assign(domValue)
6060
})
@@ -88,7 +88,10 @@ export const vModelText: ModelDirective<
8888
if(trim&&el.value.trim()===value){
8989
return
9090
}
91-
if((number||el.type==='number')&&toNumber(el.value)===value){
91+
if(
92+
(number||el.type==='number')&&
93+
looseToNumber(el.value)===value
94+
){
9295
return
9396
}
9497
}
@@ -182,7 +185,7 @@ export const vModelSelect: ModelDirective<HTMLSelectElement> ={
182185
constselectedVal=Array.prototype.filter
183186
.call(el.options,(o: HTMLOptionElement)=>o.selected)
184187
.map((o: HTMLOptionElement)=>
185-
number ? toNumber(getValue(o)) : getValue(o)
188+
number ? looseToNumber(getValue(o)) : getValue(o)
186189
)
187190
el._assign(
188191
el.multiple

‎packages/shared/src/index.ts‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,23 @@ export const def = (obj: object, key: string | symbol, value: any) =>{
153153
})
154154
}
155155

156-
exportconsttoNumber=(val: any): any=>{
156+
/**
157+
* "123-foo" will be parsed to 123
158+
* This is used for the .number modifier in v-model
159+
*/
160+
exportconstlooseToNumber=(val: any): any=>{
157161
constn=parseFloat(val)
158162
returnisNaN(n) ? val : n
159163
}
160164

165+
/**
166+
* "123-foo" will be returned as-is
167+
*/
168+
exportconsttoNumber=(val: any): any=>{
169+
constn=Number(val)
170+
returnisNaN(n) ? val : n
171+
}
172+
161173
let_globalThis: any
162174
exportconstgetGlobalThis=(): any=>{
163175
return(

0 commit comments

Comments
(0)