+return { emit }
+}
+
+})"
+`;
+
+exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type from normal script 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+
+ export interface Emits { (e: 'foo' | 'bar'): void }
+
+export default /*#__PURE__*/_defineComponent({
+ emits: [\\"foo\\", \\"bar\\"],
+ setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
+ expose();
+
+
+
return { emit }
}
})"
`;
+exports[`SFC compile <script setup> > with TypeScript > withDefaults (static) + normal script 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+
+ interface Props {
+ a?: string;
+ }
+
+export default /*#__PURE__*/_defineComponent({
+ props: {
+ a: { type: String, required: false, default: \\"a\\" }
+ },
+ setup(__props: any, { expose }) {
+ expose();
+
+const props = __props as { a: string };
+
+
+
+return { props }
+}
+
+})"
+`;
+
exports[`SFC compile <script setup> > with TypeScript > withDefaults (static) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
})
})
+ test('withDefaults (static) + normal script', () => {
+ const { content } = compile(`
+ <script lang="ts">
+ interface Props {
+ a?: string;
+ }
+ </script>
+ <script setup lang="ts">
+ const props = withDefaults(defineProps<Props>(), {
+ a: "a",
+ });
+ </script>
+ `)
+ assertCode(content)
+ })
+
// #7111
test('withDefaults (static) w/ production mode', () => {
const { content } = compile(
expect(content).toMatch(`emits: ["foo", "bar"]`)
})
+
+ test('defineEmits w/ type from normal script', () => {
+ const { content } = compile(`
+ <script lang="ts">
+ export interface Emits { (e: 'foo' | 'bar'): void }
+ </script>
+ <script setup lang="ts">
+ const emit = defineEmits<Emits>()
+ </script>
+ `)
+ assertCode(content)
+ expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
+ expect(content).toMatch(`emits: ["foo", "bar"]`)
+ })
+
test('defineEmits w/ type (type alias)', () => {
const { content } = compile(`
<script setup lang="ts">
isUsedInTemplate: boolean
}
+type FromNormalScript<T> = T & { __fromNormalScript?: boolean | null }
+type PropsDeclType = FromNormalScript<TSTypeLiteral | TSInterfaceBody>
+type EmitsDeclType = FromNormalScript<
+ TSFunctionType | TSTypeLiteral | TSInterfaceBody
+>
+
/**
* Compile `<script setup>`
* It requires the whole SFC descriptor because we need to handle and merge
let propsRuntimeDefaults: ObjectExpression | undefined
let propsDestructureDecl: Node | undefined
let propsDestructureRestId: string | undefined
- let propsTypeDecl: TSTypeLiteral | TSInterfaceBody | undefined
+ let propsTypeDecl: PropsDeclType | undefined
let propsTypeDeclRaw: Node | undefined
let propsIdentifier: string | undefined
let emitsRuntimeDecl: Node | undefined
- let emitsTypeDecl:
- | TSFunctionType
- | TSTypeLiteral
- | TSInterfaceBody
- | undefined
+ let emitsTypeDecl: EmitsDeclType | undefined
let emitsTypeDeclRaw: Node | undefined
let emitIdentifier: string | undefined
let hasAwait = false
propsTypeDecl = resolveQualifiedType(
propsTypeDeclRaw,
node => node.type === 'TSTypeLiteral'
- ) as TSTypeLiteral | TSInterfaceBody | undefined
+ ) as PropsDeclType | undefined
if (!propsTypeDecl) {
error(
emitsTypeDecl = resolveQualifiedType(
emitsTypeDeclRaw,
node => node.type === 'TSFunctionType' || node.type === 'TSTypeLiteral'
- ) as TSFunctionType | TSTypeLiteral | TSInterfaceBody | undefined
+ ) as EmitsDeclType | undefined
if (!emitsTypeDecl) {
error(
function resolveQualifiedType(
node: Node,
qualifier: (node: Node) => boolean
- ) {
+ ): Node | undefined {
if (qualifier(node)) {
return node
}
) {
const refName = node.typeName.name
const body = getAstBody()
- for (const node of body) {
+ for (let i = 0; i < body.length; i++) {
+ const node = body[i]
let qualified = isQualifiedType(
node,
qualifier,
filterExtendsType(extendsTypes, bodies)
qualified.body = bodies
}
+ ;(qualified as FromNormalScript<Node>).__fromNormalScript =
+ scriptAst && i >= scriptSetupAst.body.length
return qualified
}
}
}
}
- function genSetupPropsType(node: TSTypeLiteral | TSInterfaceBody) {
- const scriptSetupSource = scriptSetup!.content
+ function genSetupPropsType(node: PropsDeclType) {
+ const scriptSource = node.__fromNormalScript
+ ? script!.content
+ : scriptSetup!.content
if (hasStaticWithDefaults()) {
// if withDefaults() is used, we need to remove the optional flags
// on props that have default values
res +=
m.key.name +
(m.type === 'TSMethodSignature' ? '()' : '') +
- scriptSetupSource.slice(
+ scriptSource.slice(
m.typeAnnotation.start!,
m.typeAnnotation.end!
) +
', '
} else {
- res +=
- scriptSetupSource.slice(m.start!, m.typeAnnotation.end!) + `, `
+ res += scriptSource.slice(m.start!, m.typeAnnotation.end!) + `, `
}
}
}
return (res.length ? res.slice(0, -2) : res) + ` }`
} else {
- return scriptSetupSource.slice(node.start!, node.end!)
+ return scriptSource.slice(node.start!, node.end!)
}
}
if (destructureElements.length) {
args += `, { ${destructureElements.join(', ')} }`
if (emitsTypeDecl) {
- args += `: { emit: (${scriptSetup.content.slice(
+ const content = emitsTypeDecl.__fromNormalScript
+ ? script!.content
+ : scriptSetup.content
+ args += `: { emit: (${content.slice(
emitsTypeDecl.start!,
emitsTypeDecl.end!
)}), expose: any, slots: any, attrs: any }`