mirror of https://github.com/vuejs/core.git
fix(compiler-sfc): ensure script setup generates type-valid ts output
fix #4455
This commit is contained in:
parent
4cd282b0a1
commit
bacb2012ac
|
@ -704,7 +704,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type (exporte
|
||||||
export interface Emits { (e: 'foo' | 'bar'): void }
|
export interface Emits { (e: 'foo' | 'bar'): void }
|
||||||
|
|
||||||
export default /*#__PURE__*/_defineComponent({
|
export default /*#__PURE__*/_defineComponent({
|
||||||
emits: [\\"foo\\", \\"bar\\"] as unknown as undefined,
|
emits: [\\"foo\\", \\"bar\\"],
|
||||||
setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
|
setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
|
||||||
expose()
|
expose()
|
||||||
|
|
||||||
|
@ -721,7 +721,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type (exporte
|
||||||
export type Emits = { (e: 'foo' | 'bar'): void }
|
export type Emits = { (e: 'foo' | 'bar'): void }
|
||||||
|
|
||||||
export default /*#__PURE__*/_defineComponent({
|
export default /*#__PURE__*/_defineComponent({
|
||||||
emits: [\\"foo\\", \\"bar\\"] as unknown as undefined,
|
emits: [\\"foo\\", \\"bar\\"],
|
||||||
setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
|
setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
|
||||||
expose()
|
expose()
|
||||||
|
|
||||||
|
@ -738,7 +738,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type (interfa
|
||||||
interface Emits { (e: 'foo' | 'bar'): void }
|
interface Emits { (e: 'foo' | 'bar'): void }
|
||||||
|
|
||||||
export default /*#__PURE__*/_defineComponent({
|
export default /*#__PURE__*/_defineComponent({
|
||||||
emits: [\\"foo\\", \\"bar\\"] as unknown as undefined,
|
emits: [\\"foo\\", \\"bar\\"],
|
||||||
setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
|
setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
|
||||||
expose()
|
expose()
|
||||||
|
|
||||||
|
@ -755,7 +755,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type (referen
|
||||||
export type Emits = (e: 'foo' | 'bar') => void
|
export type Emits = (e: 'foo' | 'bar') => void
|
||||||
|
|
||||||
export default /*#__PURE__*/_defineComponent({
|
export default /*#__PURE__*/_defineComponent({
|
||||||
emits: [\\"foo\\", \\"bar\\"] as unknown as undefined,
|
emits: [\\"foo\\", \\"bar\\"],
|
||||||
setup(__props, { expose, emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
|
setup(__props, { expose, emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
|
||||||
expose()
|
expose()
|
||||||
|
|
||||||
|
@ -772,7 +772,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type (referen
|
||||||
type Emits = (e: 'foo' | 'bar') => void
|
type Emits = (e: 'foo' | 'bar') => void
|
||||||
|
|
||||||
export default /*#__PURE__*/_defineComponent({
|
export default /*#__PURE__*/_defineComponent({
|
||||||
emits: [\\"foo\\", \\"bar\\"] as unknown as undefined,
|
emits: [\\"foo\\", \\"bar\\"],
|
||||||
setup(__props, { expose, emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
|
setup(__props, { expose, emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
|
||||||
expose()
|
expose()
|
||||||
|
|
||||||
|
@ -789,7 +789,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type (type al
|
||||||
type Emits = { (e: 'foo' | 'bar'): void }
|
type Emits = { (e: 'foo' | 'bar'): void }
|
||||||
|
|
||||||
export default /*#__PURE__*/_defineComponent({
|
export default /*#__PURE__*/_defineComponent({
|
||||||
emits: [\\"foo\\", \\"bar\\"] as unknown as undefined,
|
emits: [\\"foo\\", \\"bar\\"],
|
||||||
setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
|
setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
|
||||||
expose()
|
expose()
|
||||||
|
|
||||||
|
@ -805,7 +805,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type (type li
|
||||||
"import { defineComponent as _defineComponent } from 'vue'
|
"import { defineComponent as _defineComponent } from 'vue'
|
||||||
|
|
||||||
export default /*#__PURE__*/_defineComponent({
|
export default /*#__PURE__*/_defineComponent({
|
||||||
emits: [\\"foo\\", \\"bar\\", \\"baz\\"] as unknown as undefined,
|
emits: [\\"foo\\", \\"bar\\", \\"baz\\"],
|
||||||
setup(__props, { expose, emit }: { emit: ({(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}), expose: any, slots: any, attrs: any }) {
|
setup(__props, { expose, emit }: { emit: ({(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}), expose: any, slots: any, attrs: any }) {
|
||||||
expose()
|
expose()
|
||||||
|
|
||||||
|
@ -821,7 +821,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type 1`] = `
|
||||||
"import { defineComponent as _defineComponent } from 'vue'
|
"import { defineComponent as _defineComponent } from 'vue'
|
||||||
|
|
||||||
export default /*#__PURE__*/_defineComponent({
|
export default /*#__PURE__*/_defineComponent({
|
||||||
emits: [\\"foo\\", \\"bar\\"] as unknown as undefined,
|
emits: [\\"foo\\", \\"bar\\"],
|
||||||
setup(__props, { expose, emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
|
setup(__props, { expose, emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
|
||||||
expose()
|
expose()
|
||||||
|
|
||||||
|
@ -840,8 +840,8 @@ export interface Props { x?: number }
|
||||||
export default /*#__PURE__*/_defineComponent({
|
export default /*#__PURE__*/_defineComponent({
|
||||||
props: {
|
props: {
|
||||||
x: { type: Number, required: false }
|
x: { type: Number, required: false }
|
||||||
} as unknown as undefined,
|
},
|
||||||
setup(__props: { x?: number }, { expose }) {
|
setup(__props: any, { expose }) {
|
||||||
expose()
|
expose()
|
||||||
|
|
||||||
|
|
||||||
|
@ -859,8 +859,8 @@ export type Props = { x?: number }
|
||||||
export default /*#__PURE__*/_defineComponent({
|
export default /*#__PURE__*/_defineComponent({
|
||||||
props: {
|
props: {
|
||||||
x: { type: Number, required: false }
|
x: { type: Number, required: false }
|
||||||
} as unknown as undefined,
|
},
|
||||||
setup(__props: { x?: number }, { expose }) {
|
setup(__props: any, { expose }) {
|
||||||
expose()
|
expose()
|
||||||
|
|
||||||
|
|
||||||
|
@ -878,8 +878,8 @@ interface Props { x?: number }
|
||||||
export default /*#__PURE__*/_defineComponent({
|
export default /*#__PURE__*/_defineComponent({
|
||||||
props: {
|
props: {
|
||||||
x: { type: Number, required: false }
|
x: { type: Number, required: false }
|
||||||
} as unknown as undefined,
|
},
|
||||||
setup(__props: { x?: number }, { expose }) {
|
setup(__props: any, { expose }) {
|
||||||
expose()
|
expose()
|
||||||
|
|
||||||
|
|
||||||
|
@ -923,34 +923,8 @@ export default /*#__PURE__*/_defineComponent({
|
||||||
literalUnionMixed: { type: [String, Number, Boolean], required: true },
|
literalUnionMixed: { type: [String, Number, Boolean], required: true },
|
||||||
intersection: { type: Object, required: true },
|
intersection: { type: Object, required: true },
|
||||||
foo: { type: [Function, null], required: true }
|
foo: { type: [Function, null], required: true }
|
||||||
} as unknown as undefined,
|
},
|
||||||
setup(__props: {
|
setup(__props: any, { expose }) {
|
||||||
string: string
|
|
||||||
number: number
|
|
||||||
boolean: boolean
|
|
||||||
object: object
|
|
||||||
objectLiteral: { a: number }
|
|
||||||
fn: (n: number) => void
|
|
||||||
functionRef: Function
|
|
||||||
objectRef: Object
|
|
||||||
array: string[]
|
|
||||||
arrayRef: Array<any>
|
|
||||||
tuple: [number, number]
|
|
||||||
set: Set<string>
|
|
||||||
literal: 'foo'
|
|
||||||
optional?: any
|
|
||||||
recordRef: Record<string, null>
|
|
||||||
interface: Test
|
|
||||||
alias: Alias
|
|
||||||
method(): void
|
|
||||||
|
|
||||||
union: string | number
|
|
||||||
literalUnion: 'foo' | 'bar'
|
|
||||||
literalUnionNumber: 1 | 2 | 3 | 4 | 5
|
|
||||||
literalUnionMixed: 'foo' | 1 | boolean
|
|
||||||
intersection: Test & {}
|
|
||||||
foo: ((item: any) => boolean) | null
|
|
||||||
}, { expose }) {
|
|
||||||
expose()
|
expose()
|
||||||
|
|
||||||
|
|
||||||
|
@ -968,8 +942,8 @@ type Props = { x?: number }
|
||||||
export default /*#__PURE__*/_defineComponent({
|
export default /*#__PURE__*/_defineComponent({
|
||||||
props: {
|
props: {
|
||||||
x: { type: Number, required: false }
|
x: { type: Number, required: false }
|
||||||
} as unknown as undefined,
|
},
|
||||||
setup(__props: { x?: number }, { expose }) {
|
setup(__props: any, { expose }) {
|
||||||
expose()
|
expose()
|
||||||
|
|
||||||
|
|
||||||
|
@ -1039,15 +1013,15 @@ export default /*#__PURE__*/_defineComponent({
|
||||||
foo: { type: String, required: false },
|
foo: { type: String, required: false },
|
||||||
bar: { type: Number, required: false },
|
bar: { type: Number, required: false },
|
||||||
baz: { type: Boolean, required: true }
|
baz: { type: Boolean, required: true }
|
||||||
}, { ...defaults }) as unknown as undefined,
|
}, { ...defaults }),
|
||||||
setup(__props: {
|
setup(__props: any, { expose }) {
|
||||||
|
expose()
|
||||||
|
|
||||||
|
const props = __props as {
|
||||||
foo?: string
|
foo?: string
|
||||||
bar?: number
|
bar?: number
|
||||||
baz: boolean
|
baz: boolean
|
||||||
}, { expose }) {
|
}
|
||||||
expose()
|
|
||||||
|
|
||||||
const props = __props
|
|
||||||
|
|
||||||
|
|
||||||
return { props, defaults }
|
return { props, defaults }
|
||||||
|
@ -1065,11 +1039,11 @@ export default /*#__PURE__*/_defineComponent({
|
||||||
bar: { type: Number, required: false },
|
bar: { type: Number, required: false },
|
||||||
baz: { type: Boolean, required: true },
|
baz: { type: Boolean, required: true },
|
||||||
qux: { type: Function, required: false, default() { return 1 } }
|
qux: { type: Function, required: false, default() { return 1 } }
|
||||||
} as unknown as undefined,
|
},
|
||||||
setup(__props: { foo: string, bar?: number, baz: boolean, qux(): number }, { expose }) {
|
setup(__props: any, { expose }) {
|
||||||
expose()
|
expose()
|
||||||
|
|
||||||
const props = __props
|
const props = __props as { foo: string, bar?: number, baz: boolean, qux(): number }
|
||||||
|
|
||||||
|
|
||||||
return { props }
|
return { props }
|
||||||
|
|
|
@ -861,7 +861,7 @@ const emit = defineEmits(['a', 'b'])
|
||||||
`)
|
`)
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
|
expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
|
||||||
expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
|
expect(content).toMatch(`emits: ["foo", "bar"]`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('defineEmits w/ type (union)', () => {
|
test('defineEmits w/ type (union)', () => {
|
||||||
|
@ -884,9 +884,7 @@ const emit = defineEmits(['a', 'b'])
|
||||||
`)
|
`)
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
expect(content).toMatch(`emit: (${type}),`)
|
expect(content).toMatch(`emit: (${type}),`)
|
||||||
expect(content).toMatch(
|
expect(content).toMatch(`emits: ["foo", "bar", "baz"]`)
|
||||||
`emits: ["foo", "bar", "baz"] as unknown as undefined`
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('defineEmits w/ type (interface)', () => {
|
test('defineEmits w/ type (interface)', () => {
|
||||||
|
@ -898,7 +896,7 @@ const emit = defineEmits(['a', 'b'])
|
||||||
`)
|
`)
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
|
expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
|
||||||
expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
|
expect(content).toMatch(`emits: ["foo", "bar"]`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('defineEmits w/ type (exported interface)', () => {
|
test('defineEmits w/ type (exported interface)', () => {
|
||||||
|
@ -910,7 +908,7 @@ const emit = defineEmits(['a', 'b'])
|
||||||
`)
|
`)
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
|
expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
|
||||||
expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
|
expect(content).toMatch(`emits: ["foo", "bar"]`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('defineEmits w/ type (type alias)', () => {
|
test('defineEmits w/ type (type alias)', () => {
|
||||||
|
@ -922,7 +920,7 @@ const emit = defineEmits(['a', 'b'])
|
||||||
`)
|
`)
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
|
expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
|
||||||
expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
|
expect(content).toMatch(`emits: ["foo", "bar"]`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('defineEmits w/ type (exported type alias)', () => {
|
test('defineEmits w/ type (exported type alias)', () => {
|
||||||
|
@ -934,7 +932,7 @@ const emit = defineEmits(['a', 'b'])
|
||||||
`)
|
`)
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
|
expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
|
||||||
expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
|
expect(content).toMatch(`emits: ["foo", "bar"]`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('defineEmits w/ type (referenced function type)', () => {
|
test('defineEmits w/ type (referenced function type)', () => {
|
||||||
|
@ -946,7 +944,7 @@ const emit = defineEmits(['a', 'b'])
|
||||||
`)
|
`)
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
|
expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
|
||||||
expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
|
expect(content).toMatch(`emits: ["foo", "bar"]`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('defineEmits w/ type (referenced exported function type)', () => {
|
test('defineEmits w/ type (referenced exported function type)', () => {
|
||||||
|
@ -958,7 +956,7 @@ const emit = defineEmits(['a', 'b'])
|
||||||
`)
|
`)
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
|
expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
|
||||||
expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
|
expect(content).toMatch(`emits: ["foo", "bar"]`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('runtime Enum', () => {
|
test('runtime Enum', () => {
|
||||||
|
|
|
@ -592,7 +592,7 @@ export function compileScript(
|
||||||
)})`
|
)})`
|
||||||
}
|
}
|
||||||
|
|
||||||
return `\n props: ${propsDecls} as unknown as undefined,`
|
return `\n props: ${propsDecls},`
|
||||||
}
|
}
|
||||||
|
|
||||||
function genSetupPropsType(node: TSTypeLiteral | TSInterfaceBody) {
|
function genSetupPropsType(node: TSTypeLiteral | TSInterfaceBody) {
|
||||||
|
@ -600,7 +600,7 @@ export function compileScript(
|
||||||
if (checkStaticDefaults()) {
|
if (checkStaticDefaults()) {
|
||||||
// if withDefaults() is used, we need to remove the optional flags
|
// if withDefaults() is used, we need to remove the optional flags
|
||||||
// on props that have default values
|
// on props that have default values
|
||||||
let res = `: { `
|
let res = `{ `
|
||||||
const members = node.type === 'TSTypeLiteral' ? node.members : node.body
|
const members = node.type === 'TSTypeLiteral' ? node.members : node.body
|
||||||
for (const m of members) {
|
for (const m of members) {
|
||||||
if (
|
if (
|
||||||
|
@ -629,7 +629,7 @@ export function compileScript(
|
||||||
}
|
}
|
||||||
return (res.length ? res.slice(0, -2) : res) + ` }`
|
return (res.length ? res.slice(0, -2) : res) + ` }`
|
||||||
} else {
|
} else {
|
||||||
return `: ${scriptSetupSource.slice(node.start!, node.end!)}`
|
return scriptSetupSource.slice(node.start!, node.end!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1051,17 +1051,25 @@ export function compileScript(
|
||||||
// 9. finalize setup() argument signature
|
// 9. finalize setup() argument signature
|
||||||
let args = `__props`
|
let args = `__props`
|
||||||
if (propsTypeDecl) {
|
if (propsTypeDecl) {
|
||||||
args += genSetupPropsType(propsTypeDecl)
|
// mark as any and only cast on assignment
|
||||||
|
// since the user defined complex types may be incompatible with the
|
||||||
|
// inferred type from generated runtime declarations
|
||||||
|
args += `: any`
|
||||||
}
|
}
|
||||||
// inject user assignment of props
|
// inject user assignment of props
|
||||||
// we use a default __props so that template expressions referencing props
|
// we use a default __props so that template expressions referencing props
|
||||||
// can use it directly
|
// can use it directly
|
||||||
if (propsIdentifier) {
|
if (propsIdentifier) {
|
||||||
s.prependRight(startOffset, `\nconst ${propsIdentifier} = __props`)
|
s.prependRight(
|
||||||
|
startOffset,
|
||||||
|
`\nconst ${propsIdentifier} = __props${
|
||||||
|
propsTypeDecl ? ` as ${genSetupPropsType(propsTypeDecl)}` : ``
|
||||||
|
}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// inject temp variables for async context preservation
|
// inject temp variables for async context preservation
|
||||||
if (hasAwait) {
|
if (hasAwait) {
|
||||||
const any = isTS ? `:any` : ``
|
const any = isTS ? `: any` : ``
|
||||||
s.prependRight(startOffset, `\nlet __temp${any}, __restore${any}\n`)
|
s.prependRight(startOffset, `\nlet __temp${any}, __restore${any}\n`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1589,7 +1597,7 @@ function genRuntimeEmits(emits: Set<string>) {
|
||||||
return emits.size
|
return emits.size
|
||||||
? `\n emits: [${Array.from(emits)
|
? `\n emits: [${Array.from(emits)
|
||||||
.map(p => JSON.stringify(p))
|
.map(p => JSON.stringify(p))
|
||||||
.join(', ')}] as unknown as undefined,`
|
.join(', ')}],`
|
||||||
: ``
|
: ``
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue