From: Tony Trinh Date: Mon, 14 Nov 2022 01:13:32 +0000 (-0600) Subject: types(runtime-core): support plugin options type inference (#3969) X-Git-Tag: v3.2.46~87 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c513126c5dd12fec1064bf2df53384099f4000d2;p=thirdparty%2Fvuejs%2Fcore.git types(runtime-core): support plugin options type inference (#3969) --- diff --git a/packages/runtime-core/src/apiCreateApp.ts b/packages/runtime-core/src/apiCreateApp.ts index 1c588dbc8f..c02597bed5 100644 --- a/packages/runtime-core/src/apiCreateApp.ts +++ b/packages/runtime-core/src/apiCreateApp.ts @@ -31,7 +31,13 @@ import { ObjectEmitsOptions } from './componentEmits' export interface App { version: string config: AppConfig - use(plugin: Plugin, ...options: any[]): this + + use( + plugin: Plugin, + ...options: Options + ): this + use(plugin: Plugin, options: Options): this + mixin(mixin: ComponentOptions): this component(name: string): Component | undefined component(name: string, component: Component): this @@ -140,12 +146,16 @@ export interface AppContext { filters?: Record } -type PluginInstallFunction = (app: App, ...options: any[]) => any +type PluginInstallFunction = Options extends unknown[] + ? (app: App, ...options: Options) => any + : (app: App, options: Options) => any -export type Plugin = - | (PluginInstallFunction & { install?: PluginInstallFunction }) +export type Plugin = + | (PluginInstallFunction & { + install?: PluginInstallFunction + }) | { - install: PluginInstallFunction + install: PluginInstallFunction } export function createAppContext(): AppContext { diff --git a/test-dts/appUse.test-d.ts b/test-dts/appUse.test-d.ts new file mode 100644 index 0000000000..526aa888a0 --- /dev/null +++ b/test-dts/appUse.test-d.ts @@ -0,0 +1,95 @@ +import { createApp, App, Plugin } from './index' + +const app = createApp({}) + +// Plugin without types accept anything +const PluginWithoutType: Plugin = { + install(app: App) {} +} + +app.use(PluginWithoutType) +app.use(PluginWithoutType, 2) +app.use(PluginWithoutType, { anything: 'goes' }, true) + +type PluginOptions = { + option1?: string + option2: number + option3: boolean +} + +const PluginWithObjectOptions = { + install(app: App, options: PluginOptions) { + options.option1 + options.option2 + options.option3 + } +} + +for (const Plugin of [ + PluginWithObjectOptions, + PluginWithObjectOptions.install +]) { + // @ts-expect-error: no params + app.use(Plugin) + + // @ts-expect-error option2 and option3 (required) missing + app.use(Plugin, {}) + // @ts-expect-error type mismatch + app.use(Plugin, undefined) + // valid options + app.use(Plugin, { option2: 1, option3: true }) + app.use(Plugin, { option1: 'foo', option2: 1, option3: true }) +} + +const PluginNoOptions = { + install(app: App) {} +} + +for (const Plugin of [PluginNoOptions, PluginNoOptions.install]) { + // no args + app.use(Plugin) + // @ts-expect-error unexpected plugin option + app.use(Plugin, {}) + // @ts-expect-error only no options is valid + app.use(Plugin, undefined) +} + +const PluginMultipleArgs = { + install: (app: App, a: string, b: number) => {} +} + +for (const Plugin of [PluginMultipleArgs, PluginMultipleArgs.install]) { + // @ts-expect-error: 2 arguments expected + app.use(Plugin, 'hey') + app.use(Plugin, 'hey', 2) +} + +const PluginOptionalOptions = { + install( + app: App, + options: PluginOptions = { option2: 2, option3: true, option1: 'foo' } + ) { + options.option1 + options.option2 + options.option3 + } +} + +for (const Plugin of [PluginOptionalOptions, PluginOptionalOptions.install]) { + // both version are valid + app.use(Plugin) + app.use(Plugin, undefined) + + // @ts-expect-error option2 and option3 (required) missing + app.use(Plugin, {}) + // valid options + app.use(Plugin, { option2: 1, option3: true }) + app.use(Plugin, { option1: 'foo', option2: 1, option3: true }) +} + +// still valid but it's better to use the regular function because this one can accept an optional param +const PluginTyped: Plugin = (app, options) => {} + +// @ts-expect-error: needs options +app.use(PluginTyped) +app.use(PluginTyped, { option2: 2, option3: true })