diff --git a/.changeset/red-waves-tease.md b/.changeset/red-waves-tease.md new file mode 100644 index 00000000..6abab5ce --- /dev/null +++ b/.changeset/red-waves-tease.md @@ -0,0 +1,6 @@ +--- +'@vue-macros/better-define': patch +'@vue-macros/api': patch +--- + +infer TS `Extract` and `Exclude` runtime type diff --git a/packages/api/src/vue/utils.ts b/packages/api/src/vue/utils.ts index 8a2b0fa8..5e6b0432 100644 --- a/packages/api/src/vue/utils.ts +++ b/packages/api/src/vue/utils.ts @@ -67,11 +67,36 @@ export async function inferRuntimeType( case 'Readonly': case 'Pick': case 'Omit': - case 'Exclude': - case 'Extract': case 'Required': case 'InstanceType': return ['Object'] + + case 'Extract': + if ( + node.type.typeParameters && + node.type.typeParameters.params[1] + ) { + const t = await resolveTSReferencedType({ + scope: node.scope, + type: node.type.typeParameters.params[1], + }) + if (isTSExports(t)) return ['Object'] + else if (t) return inferRuntimeType(t) + } + return ['null'] + case 'Exclude': + if ( + node.type.typeParameters && + node.type.typeParameters.params[0] + ) { + const t = await resolveTSReferencedType({ + scope: node.scope, + type: node.type.typeParameters.params[0], + }) + if (isTSExports(t)) return ['Object'] + else if (t) return inferRuntimeType(t) + } + return ['null'] } } return [`null`] diff --git a/packages/better-define/tests/__snapshots__/fixtures.test.ts.snap b/packages/better-define/tests/__snapshots__/fixtures.test.ts.snap index b5cf1083..100f8904 100644 --- a/packages/better-define/tests/__snapshots__/fixtures.test.ts.snap +++ b/packages/better-define/tests/__snapshots__/fixtures.test.ts.snap @@ -586,6 +586,64 @@ export { specialKey as default }; " `; +exports[`fixtures > tests/fixtures/ts-utility-types.vue > isProduction = false 1`] = ` +"import { defineComponent } from 'vue'; + +var _sfc_main = /* @__PURE__ */ defineComponent({ + __name: \\"ts-utility-types\\", + props: { + \\"foo\\": { type: String, required: true }, + \\"bar\\": { type: String, required: true } + }, + setup(__props) { + return () => { + }; + } +}); + +var _export_sfc = (sfc, props) => { + const target = sfc.__vccOpts || sfc; + for (const [key, val] of props) { + target[key] = val; + } + return target; +}; + +var tsUtilityTypes = /* @__PURE__ */ _export_sfc(_sfc_main, [__FILE__]); + +export { tsUtilityTypes as default }; +" +`; + +exports[`fixtures > tests/fixtures/ts-utility-types.vue > isProduction = true 1`] = ` +"import { defineComponent } from 'vue'; + +var _sfc_main = /* @__PURE__ */ defineComponent({ + __name: \\"ts-utility-types\\", + props: { + \\"foo\\": null, + \\"bar\\": null + }, + setup(__props) { + return () => { + }; + } +}); + +var _export_sfc = (sfc, props) => { + const target = sfc.__vccOpts || sfc; + for (const [key, val] of props) { + target[key] = val; + } + return target; +}; + +var tsUtilityTypes = /* @__PURE__ */ _export_sfc(_sfc_main, [__FILE__]); + +export { tsUtilityTypes as default }; +" +`; + exports[`fixtures > tests/fixtures/union.vue > isProduction = false 1`] = ` "import { defineComponent } from 'vue'; diff --git a/packages/better-define/tests/fixtures/ts-utility-types.vue b/packages/better-define/tests/fixtures/ts-utility-types.vue new file mode 100644 index 00000000..52584d86 --- /dev/null +++ b/packages/better-define/tests/fixtures/ts-utility-types.vue @@ -0,0 +1,14 @@ +