Skip to content

Commit 4253a57

Browse files
committed
fix(defineModel): align prod mode runtime type generation with defineProps
close #10769
1 parent 3724693 commit 4253a57

File tree

3 files changed

+100
-31
lines changed

3 files changed

+100
-31
lines changed

packages/compiler-sfc/__tests__/compileScript/__snapshots__/defineModel.spec.ts.snap

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,43 @@ return { modelValue, fn, fnWithDefault, str, optional }
226226
227227
})"
228228
`;
229+
230+
exports[`defineModel() > w/ types, production mode, boolean + multiple types 1`] = `
231+
"import { useModel as _useModel, defineComponent as _defineComponent } from 'vue'
232+
233+
export default /*#__PURE__*/_defineComponent({
234+
props: {
235+
"modelValue": { type: [Boolean, String, Object] },
236+
"modelModifiers": {},
237+
},
238+
emits: ["update:modelValue"],
239+
setup(__props, { expose: __expose }) {
240+
__expose();
241+
242+
const modelValue = _useModel<boolean | string | {}>(__props, "modelValue")
243+
244+
return { modelValue }
245+
}
246+
247+
})"
248+
`;
249+
250+
exports[`defineModel() > w/ types, production mode, function + runtime opts + multiple types 1`] = `
251+
"import { useModel as _useModel, defineComponent as _defineComponent } from 'vue'
252+
253+
export default /*#__PURE__*/_defineComponent({
254+
props: {
255+
"modelValue": { type: [Number, Function], ...{ default: () => 1 } },
256+
"modelModifiers": {},
257+
},
258+
emits: ["update:modelValue"],
259+
setup(__props, { expose: __expose }) {
260+
__expose();
261+
262+
const modelValue = _useModel<number | (() => number)>(__props, "modelValue")
263+
264+
return { modelValue }
265+
}
266+
267+
})"
268+
`;

packages/compiler-sfc/__tests__/compileScript/defineModel.spec.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,34 @@ describe('defineModel()', () => {
161161
})
162162
})
163163

164+
test('w/ types, production mode, boolean + multiple types', () => {
165+
const { content } = compile(
166+
`
167+
<script setup lang="ts">
168+
const modelValue = defineModel<boolean | string | {}>()
169+
</script>
170+
`,
171+
{ isProd: true },
172+
)
173+
assertCode(content)
174+
expect(content).toMatch('"modelValue": { type: [Boolean, String, Object] }')
175+
})
176+
177+
test('w/ types, production mode, function + runtime opts + multiple types', () => {
178+
const { content } = compile(
179+
`
180+
<script setup lang="ts">
181+
const modelValue = defineModel<number | (() => number)>({ default: () => 1 })
182+
</script>
183+
`,
184+
{ isProd: true },
185+
)
186+
assertCode(content)
187+
expect(content).toMatch(
188+
'"modelValue": { type: [Number, Function], ...{ default: () => 1 } }',
189+
)
190+
})
191+
164192
test('get / set transformers', () => {
165193
const { content } = compile(
166194
`

packages/compiler-sfc/src/script/defineModel.ts

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
import type { LVal, Node, TSType } from '@babel/types'
22
import type { ScriptCompileContext } from './context'
33
import { inferRuntimeType } from './resolveType'
4-
import {
5-
UNKNOWN_TYPE,
6-
concatStrings,
7-
isCallOf,
8-
toRuntimeTypeString,
9-
} from './utils'
4+
import { UNKNOWN_TYPE, isCallOf, toRuntimeTypeString } from './utils'
105
import { BindingTypes, unwrapTSNode } from '@vue/compiler-dom'
116

127
export const DEFINE_MODEL = 'defineModel'
@@ -124,44 +119,50 @@ export function genModelProps(ctx: ScriptCompileContext) {
124119

125120
const isProd = !!ctx.options.isProd
126121
let modelPropsDecl = ''
127-
for (const [name, { type, options }] of Object.entries(ctx.modelDecls)) {
122+
for (const [name, { type, options: runtimeOptions }] of Object.entries(
123+
ctx.modelDecls,
124+
)) {
128125
let skipCheck = false
129-
126+
let codegenOptions = ``
130127
let runtimeTypes = type && inferRuntimeType(ctx, type)
131128
if (runtimeTypes) {
132129
const hasBoolean = runtimeTypes.includes('Boolean')
130+
const hasFunction = runtimeTypes.includes('Function')
133131
const hasUnknownType = runtimeTypes.includes(UNKNOWN_TYPE)
134132

135-
if (isProd || hasUnknownType) {
136-
runtimeTypes = runtimeTypes.filter(
137-
t =>
138-
t === 'Boolean' ||
139-
(hasBoolean && t === 'String') ||
140-
(t === 'Function' && options),
141-
)
133+
if (hasUnknownType) {
134+
if (hasBoolean || hasFunction) {
135+
runtimeTypes = runtimeTypes.filter(t => t !== UNKNOWN_TYPE)
136+
skipCheck = true
137+
} else {
138+
runtimeTypes = ['null']
139+
}
140+
}
142141

143-
skipCheck = !isProd && hasUnknownType && runtimeTypes.length > 0
142+
if (!isProd) {
143+
codegenOptions =
144+
`type: ${toRuntimeTypeString(runtimeTypes)}` +
145+
(skipCheck ? ', skipCheck: true' : '')
146+
} else if (hasBoolean || (runtimeOptions && hasFunction)) {
147+
// preserve types if contains boolean, or
148+
// function w/ runtime options that may contain default
149+
codegenOptions = `type: ${toRuntimeTypeString(runtimeTypes)}`
150+
} else {
151+
// able to drop types in production
144152
}
145153
}
146154

147-
let runtimeType =
148-
(runtimeTypes &&
149-
runtimeTypes.length > 0 &&
150-
toRuntimeTypeString(runtimeTypes)) ||
151-
undefined
152-
153-
const codegenOptions = concatStrings([
154-
runtimeType && `type: ${runtimeType}`,
155-
skipCheck && 'skipCheck: true',
156-
])
157-
158155
let decl: string
159-
if (runtimeType && options) {
156+
if (codegenOptions && runtimeOptions) {
160157
decl = ctx.isTS
161-
? `{ ${codegenOptions}, ...${options} }`
162-
: `Object.assign({ ${codegenOptions} }, ${options})`
158+
? `{ ${codegenOptions}, ...${runtimeOptions} }`
159+
: `Object.assign({ ${codegenOptions} }, ${runtimeOptions})`
160+
} else if (codegenOptions) {
161+
decl = `{ ${codegenOptions} }`
162+
} else if (runtimeOptions) {
163+
decl = runtimeOptions
163164
} else {
164-
decl = options || (runtimeType ? `{ ${codegenOptions} }` : '{}')
165+
decl = `{}`
165166
}
166167
modelPropsDecl += `\n ${JSON.stringify(name)}: ${decl},`
167168

0 commit comments

Comments
 (0)