Skip to content

Commit bacb201

Browse files
committed
fix(compiler-sfc): ensure script setup generates type-valid ts output
fix #4455
1 parent 4cd282b commit bacb201

File tree

3 files changed

+50
-70
lines changed

3 files changed

+50
-70
lines changed

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

+27-53
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type (exporte
704704
export interface Emits { (e: 'foo' | 'bar'): void }
705705
706706
export default /*#__PURE__*/_defineComponent({
707-
emits: [\\"foo\\", \\"bar\\"] as unknown as undefined,
707+
emits: [\\"foo\\", \\"bar\\"],
708708
setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
709709
expose()
710710
@@ -721,7 +721,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type (exporte
721721
export type Emits = { (e: 'foo' | 'bar'): void }
722722
723723
export default /*#__PURE__*/_defineComponent({
724-
emits: [\\"foo\\", \\"bar\\"] as unknown as undefined,
724+
emits: [\\"foo\\", \\"bar\\"],
725725
setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
726726
expose()
727727
@@ -738,7 +738,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type (interfa
738738
interface Emits { (e: 'foo' | 'bar'): void }
739739
740740
export default /*#__PURE__*/_defineComponent({
741-
emits: [\\"foo\\", \\"bar\\"] as unknown as undefined,
741+
emits: [\\"foo\\", \\"bar\\"],
742742
setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
743743
expose()
744744
@@ -755,7 +755,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type (referen
755755
export type Emits = (e: 'foo' | 'bar') => void
756756
757757
export default /*#__PURE__*/_defineComponent({
758-
emits: [\\"foo\\", \\"bar\\"] as unknown as undefined,
758+
emits: [\\"foo\\", \\"bar\\"],
759759
setup(__props, { expose, emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
760760
expose()
761761
@@ -772,7 +772,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type (referen
772772
type Emits = (e: 'foo' | 'bar') => void
773773
774774
export default /*#__PURE__*/_defineComponent({
775-
emits: [\\"foo\\", \\"bar\\"] as unknown as undefined,
775+
emits: [\\"foo\\", \\"bar\\"],
776776
setup(__props, { expose, emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
777777
expose()
778778
@@ -789,7 +789,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type (type al
789789
type Emits = { (e: 'foo' | 'bar'): void }
790790
791791
export default /*#__PURE__*/_defineComponent({
792-
emits: [\\"foo\\", \\"bar\\"] as unknown as undefined,
792+
emits: [\\"foo\\", \\"bar\\"],
793793
setup(__props, { expose, emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
794794
expose()
795795
@@ -805,7 +805,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type (type li
805805
"import { defineComponent as _defineComponent } from 'vue'
806806
807807
export default /*#__PURE__*/_defineComponent({
808-
emits: [\\"foo\\", \\"bar\\", \\"baz\\"] as unknown as undefined,
808+
emits: [\\"foo\\", \\"bar\\", \\"baz\\"],
809809
setup(__props, { expose, emit }: { emit: ({(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}), expose: any, slots: any, attrs: any }) {
810810
expose()
811811
@@ -821,7 +821,7 @@ exports[`SFC compile <script setup> with TypeScript defineEmits w/ type 1`] = `
821821
"import { defineComponent as _defineComponent } from 'vue'
822822
823823
export default /*#__PURE__*/_defineComponent({
824-
emits: [\\"foo\\", \\"bar\\"] as unknown as undefined,
824+
emits: [\\"foo\\", \\"bar\\"],
825825
setup(__props, { expose, emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
826826
expose()
827827
@@ -840,8 +840,8 @@ export interface Props { x?: number }
840840
export default /*#__PURE__*/_defineComponent({
841841
props: {
842842
x: { type: Number, required: false }
843-
} as unknown as undefined,
844-
setup(__props: { x?: number }, { expose }) {
843+
},
844+
setup(__props: any, { expose }) {
845845
expose()
846846
847847
@@ -859,8 +859,8 @@ export type Props = { x?: number }
859859
export default /*#__PURE__*/_defineComponent({
860860
props: {
861861
x: { type: Number, required: false }
862-
} as unknown as undefined,
863-
setup(__props: { x?: number }, { expose }) {
862+
},
863+
setup(__props: any, { expose }) {
864864
expose()
865865
866866
@@ -878,8 +878,8 @@ interface Props { x?: number }
878878
export default /*#__PURE__*/_defineComponent({
879879
props: {
880880
x: { type: Number, required: false }
881-
} as unknown as undefined,
882-
setup(__props: { x?: number }, { expose }) {
881+
},
882+
setup(__props: any, { expose }) {
883883
expose()
884884
885885
@@ -923,34 +923,8 @@ export default /*#__PURE__*/_defineComponent({
923923
literalUnionMixed: { type: [String, Number, Boolean], required: true },
924924
intersection: { type: Object, required: true },
925925
foo: { type: [Function, null], required: true }
926-
} as unknown as undefined,
927-
setup(__props: {
928-
string: string
929-
number: number
930-
boolean: boolean
931-
object: object
932-
objectLiteral: { a: number }
933-
fn: (n: number) => void
934-
functionRef: Function
935-
objectRef: Object
936-
array: string[]
937-
arrayRef: Array<any>
938-
tuple: [number, number]
939-
set: Set<string>
940-
literal: 'foo'
941-
optional?: any
942-
recordRef: Record<string, null>
943-
interface: Test
944-
alias: Alias
945-
method(): void
946-
947-
union: string | number
948-
literalUnion: 'foo' | 'bar'
949-
literalUnionNumber: 1 | 2 | 3 | 4 | 5
950-
literalUnionMixed: 'foo' | 1 | boolean
951-
intersection: Test & {}
952-
foo: ((item: any) => boolean) | null
953-
}, { expose }) {
926+
},
927+
setup(__props: any, { expose }) {
954928
expose()
955929
956930
@@ -968,8 +942,8 @@ type Props = { x?: number }
968942
export default /*#__PURE__*/_defineComponent({
969943
props: {
970944
x: { type: Number, required: false }
971-
} as unknown as undefined,
972-
setup(__props: { x?: number }, { expose }) {
945+
},
946+
setup(__props: any, { expose }) {
973947
expose()
974948
975949
@@ -1039,15 +1013,15 @@ export default /*#__PURE__*/_defineComponent({
10391013
foo: { type: String, required: false },
10401014
bar: { type: Number, required: false },
10411015
baz: { type: Boolean, required: true }
1042-
}, { ...defaults }) as unknown as undefined,
1043-
setup(__props: {
1016+
}, { ...defaults }),
1017+
setup(__props: any, { expose }) {
1018+
expose()
1019+
1020+
const props = __props as {
10441021
foo?: string
10451022
bar?: number
10461023
baz: boolean
1047-
}, { expose }) {
1048-
expose()
1049-
1050-
const props = __props
1024+
}
10511025
10521026
10531027
return { props, defaults }
@@ -1065,11 +1039,11 @@ export default /*#__PURE__*/_defineComponent({
10651039
bar: { type: Number, required: false },
10661040
baz: { type: Boolean, required: true },
10671041
qux: { type: Function, required: false, default() { return 1 } }
1068-
} as unknown as undefined,
1069-
setup(__props: { foo: string, bar?: number, baz: boolean, qux(): number }, { expose }) {
1042+
},
1043+
setup(__props: any, { expose }) {
10701044
expose()
10711045
1072-
const props = __props
1046+
const props = __props as { foo: string, bar?: number, baz: boolean, qux(): number }
10731047
10741048
10751049
return { props }

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

+8-10
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,7 @@ const emit = defineEmits(['a', 'b'])
861861
`)
862862
assertCode(content)
863863
expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
864-
expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
864+
expect(content).toMatch(`emits: ["foo", "bar"]`)
865865
})
866866

867867
test('defineEmits w/ type (union)', () => {
@@ -884,9 +884,7 @@ const emit = defineEmits(['a', 'b'])
884884
`)
885885
assertCode(content)
886886
expect(content).toMatch(`emit: (${type}),`)
887-
expect(content).toMatch(
888-
`emits: ["foo", "bar", "baz"] as unknown as undefined`
889-
)
887+
expect(content).toMatch(`emits: ["foo", "bar", "baz"]`)
890888
})
891889

892890
test('defineEmits w/ type (interface)', () => {
@@ -898,7 +896,7 @@ const emit = defineEmits(['a', 'b'])
898896
`)
899897
assertCode(content)
900898
expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
901-
expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
899+
expect(content).toMatch(`emits: ["foo", "bar"]`)
902900
})
903901

904902
test('defineEmits w/ type (exported interface)', () => {
@@ -910,7 +908,7 @@ const emit = defineEmits(['a', 'b'])
910908
`)
911909
assertCode(content)
912910
expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
913-
expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
911+
expect(content).toMatch(`emits: ["foo", "bar"]`)
914912
})
915913

916914
test('defineEmits w/ type (type alias)', () => {
@@ -922,7 +920,7 @@ const emit = defineEmits(['a', 'b'])
922920
`)
923921
assertCode(content)
924922
expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
925-
expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
923+
expect(content).toMatch(`emits: ["foo", "bar"]`)
926924
})
927925

928926
test('defineEmits w/ type (exported type alias)', () => {
@@ -934,7 +932,7 @@ const emit = defineEmits(['a', 'b'])
934932
`)
935933
assertCode(content)
936934
expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
937-
expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
935+
expect(content).toMatch(`emits: ["foo", "bar"]`)
938936
})
939937

940938
test('defineEmits w/ type (referenced function type)', () => {
@@ -946,7 +944,7 @@ const emit = defineEmits(['a', 'b'])
946944
`)
947945
assertCode(content)
948946
expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
949-
expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
947+
expect(content).toMatch(`emits: ["foo", "bar"]`)
950948
})
951949

952950
test('defineEmits w/ type (referenced exported function type)', () => {
@@ -958,7 +956,7 @@ const emit = defineEmits(['a', 'b'])
958956
`)
959957
assertCode(content)
960958
expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
961-
expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
959+
expect(content).toMatch(`emits: ["foo", "bar"]`)
962960
})
963961

964962
test('runtime Enum', () => {

packages/compiler-sfc/src/compileScript.ts

+15-7
Original file line numberDiff line numberDiff line change
@@ -592,15 +592,15 @@ export function compileScript(
592592
)})`
593593
}
594594

595-
return `\n props: ${propsDecls} as unknown as undefined,`
595+
return `\n props: ${propsDecls},`
596596
}
597597

598598
function genSetupPropsType(node: TSTypeLiteral | TSInterfaceBody) {
599599
const scriptSetupSource = scriptSetup!.content
600600
if (checkStaticDefaults()) {
601601
// if withDefaults() is used, we need to remove the optional flags
602602
// on props that have default values
603-
let res = `: { `
603+
let res = `{ `
604604
const members = node.type === 'TSTypeLiteral' ? node.members : node.body
605605
for (const m of members) {
606606
if (
@@ -629,7 +629,7 @@ export function compileScript(
629629
}
630630
return (res.length ? res.slice(0, -2) : res) + ` }`
631631
} else {
632-
return `: ${scriptSetupSource.slice(node.start!, node.end!)}`
632+
return scriptSetupSource.slice(node.start!, node.end!)
633633
}
634634
}
635635

@@ -1051,17 +1051,25 @@ export function compileScript(
10511051
// 9. finalize setup() argument signature
10521052
let args = `__props`
10531053
if (propsTypeDecl) {
1054-
args += genSetupPropsType(propsTypeDecl)
1054+
// mark as any and only cast on assignment
1055+
// since the user defined complex types may be incompatible with the
1056+
// inferred type from generated runtime declarations
1057+
args += `: any`
10551058
}
10561059
// inject user assignment of props
10571060
// we use a default __props so that template expressions referencing props
10581061
// can use it directly
10591062
if (propsIdentifier) {
1060-
s.prependRight(startOffset, `\nconst ${propsIdentifier} = __props`)
1063+
s.prependRight(
1064+
startOffset,
1065+
`\nconst ${propsIdentifier} = __props${
1066+
propsTypeDecl ? ` as ${genSetupPropsType(propsTypeDecl)}` : ``
1067+
}`
1068+
)
10611069
}
10621070
// inject temp variables for async context preservation
10631071
if (hasAwait) {
1064-
const any = isTS ? `:any` : ``
1072+
const any = isTS ? `: any` : ``
10651073
s.prependRight(startOffset, `\nlet __temp${any}, __restore${any}\n`)
10661074
}
10671075

@@ -1589,7 +1597,7 @@ function genRuntimeEmits(emits: Set<string>) {
15891597
return emits.size
15901598
? `\n emits: [${Array.from(emits)
15911599
.map(p => JSON.stringify(p))
1592-
.join(', ')}] as unknown as undefined,`
1600+
.join(', ')}],`
15931601
: ``
15941602
}
15951603

0 commit comments

Comments
 (0)