Skip to content

Commit 6b32b9c

Browse files
authored
fix(definition): infer lazy args from inline definitions (#585)
1 parent 789136d commit 6b32b9c

2 files changed

Lines changed: 78 additions & 19 deletions

File tree

packages/gunshi/src/definition.test-d.ts

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,9 @@ describe('define', () => {
6363
expectTypeOf(ctx.values.count).toEqualTypeOf<number | undefined>()
6464
expectTypeOf(ctx.values.verbose).toEqualTypeOf<boolean>()
6565
expectTypeOf(ctx.extensions[loggerPluginId]).toEqualTypeOf<LoggerExtension>()
66-
// @ts-expect-error unknown plugin extension should not be available
67-
ctx.extensions.missing
66+
expectTypeOf(ctx.extensions)
67+
// @ts-expect-error unknown plugin extension should not be available
68+
.toHaveProperty('missing')
6869
}
6970
})
7071
})
@@ -80,8 +81,9 @@ describe('defineWithTypes', () => {
8081
expectTypeOf(ctx.values.count).toEqualTypeOf<number | undefined>()
8182
expectTypeOf(ctx.values.verbose).toEqualTypeOf<boolean>()
8283
expectTypeOf(ctx.extensions[loggerPluginId]).toEqualTypeOf<LoggerExtension>()
83-
// @ts-expect-error unknown plugin extension should not be available
84-
ctx.extensions.missing
84+
expectTypeOf(ctx.extensions)
85+
// @ts-expect-error unknown plugin extension should not be available
86+
.toHaveProperty('missing')
8587
}
8688
})
8789

@@ -116,6 +118,41 @@ describe('defineWithTypes', () => {
116118
})
117119

118120
describe('lazy', () => {
121+
test('infers loader context from inline definition without explicit extensions generic (Issue #582)', () => {
122+
lazy(
123+
() => {
124+
return ({ values }) => {
125+
const test = values.test
126+
expectTypeOf(test).toEqualTypeOf<boolean | undefined>()
127+
}
128+
},
129+
{
130+
name: 'main',
131+
args: {
132+
test: {
133+
type: 'boolean'
134+
}
135+
}
136+
}
137+
)
138+
})
139+
140+
test('infers standard arg value types from inline definition', () => {
141+
lazy(
142+
() => {
143+
return ({ values }) => {
144+
expectTypeOf(values.input).toEqualTypeOf<string>()
145+
expectTypeOf(values.count).toEqualTypeOf<number | undefined>()
146+
expectTypeOf(values.verbose).toEqualTypeOf<boolean>()
147+
}
148+
},
149+
{
150+
name: 'deploy',
151+
args: commandArgs
152+
}
153+
)
154+
})
155+
119156
test('infers loader context from CommandRunner annotation', () => {
120157
const lazyCommand = lazy((): CommandRunner<{ args: typeof commandArgs; extensions: {} }> => {
121158
return ctx => {
@@ -129,6 +166,19 @@ describe('lazy', () => {
129166
expectTypeOf(lazyCommand.commandName).toEqualTypeOf<string | undefined>()
130167
})
131168

169+
test('preserves inline definition metadata without args', () => {
170+
const lazyCommand = lazy(
171+
() => {
172+
return () => {}
173+
},
174+
{
175+
name: 'lazy'
176+
}
177+
)
178+
179+
expectTypeOf(lazyCommand.commandName).toEqualTypeOf<string>()
180+
})
181+
132182
test('preserves inline definition metadata', () => {
133183
const lazyCommand = lazy(
134184
(): CommandRunner<{ args: typeof commandArgs; extensions: {} }> => {

packages/gunshi/src/definition.ts

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ import type {
4343
ExtendContext,
4444
ExtractArgs,
4545
ExtractExtensions,
46-
GunshiParams,
4746
GunshiParamsConstraint,
4847
LazyCommand,
4948
NormalizeToGunshiParams,
@@ -153,7 +152,7 @@ type DefineWithTypesReturn<DefaultExtensions extends ExtendContext, DefaultArgs
153152
/**
154153
* Define a {@link Command | command} with types
155154
*
156-
* This helper function allows specifying the type parameter of {@link GunshiParams}
155+
* This helper function allows specifying the type parameter of {@link GunshiParamsConstraint}
157156
* while inferring the {@link Args} type, {@link ExtendContext} type from the definition.
158157
*
159158
* @example
@@ -173,7 +172,7 @@ type DefineWithTypesReturn<DefaultExtensions extends ExtendContext, DefaultArgs
173172
* })
174173
* ```
175174
*
176-
* @typeParam G - A {@link GunshiParams} type
175+
* @typeParam G - A {@link GunshiParamsConstraint} type
177176
*
178177
* @returns A function that takes a command definition via {@link define}
179178
*
@@ -246,7 +245,23 @@ export function lazy<A extends Args>(
246245
* }, testDefinition)
247246
* ```
248247
*
249-
* @typeParam A - An {@link Args} type
248+
* @typeParam D - A partial {@link Command} definition type with required `args`
249+
*
250+
* @param loader - A {@link CommandLoader | command loader} function that returns a command definition
251+
* @param definition - A {@link Command | command} definition
252+
* @returns A {@link LazyCommand | lazy command} that can be executed later
253+
*/
254+
export function lazy<
255+
D extends { args: Args } & Partial<Command<{ args: D['args']; extensions: {} }>>
256+
>(
257+
loader: CommandLoader<{ args: D['args']; extensions: {} }>,
258+
definition: D
259+
): LazyCommand<{ args: D['args']; extensions: {} }, D>
260+
261+
/**
262+
* Define a {@link LazyCommand | lazy command} with explicit Gunshi parameters and optional definition.
263+
*
264+
* @typeParam G - A {@link GunshiParamsConstraint}
250265
* @typeParam D - A partial {@link Command} definition type
251266
*
252267
* @param loader - A {@link CommandLoader | command loader} function that returns a command definition
@@ -255,14 +270,8 @@ export function lazy<A extends Args>(
255270
*/
256271
export function lazy<
257272
G extends GunshiParamsConstraint = DefaultGunshiParams,
258-
A extends ExtractArgs<G> = ExtractArgs<G>,
259-
D extends Partial<Command<{ args: A; extensions: {} }>> = Partial<
260-
Command<{ args: A; extensions: {} }>
261-
>
262-
>(
263-
loader: CommandLoader<{ args: A; extensions: {} }>,
264-
definition: D
265-
): LazyCommand<{ args: A; extensions: {} }, D>
273+
D extends Partial<Command<G>> = Partial<Command<G>>
274+
>(loader: CommandLoader<G>, definition?: D): LazyCommand<G, D>
266275

267276
/**
268277
* Define a {@link LazyCommand | lazy command} with or without definition.
@@ -301,7 +310,7 @@ export function lazy<G extends GunshiParamsConstraint = DefaultGunshiParams>(
301310
/**
302311
* Return type for lazyWithTypes
303312
*
304-
* @typeParam FullG - The normalized {@link GunshiParams} type
313+
* @typeParam FullG - The normalized {@link GunshiParamsConstraint} type
305314
*
306315
* @internal
307316
*/
@@ -315,7 +324,7 @@ type LazyWithTypesReturn<FullG extends GunshiParamsConstraint> = <
315324
/**
316325
* Define a {@link LazyCommand | lazy command} with specific type parameters.
317326
*
318-
* This helper function allows specifying the type parameter of {@link GunshiParams}
327+
* This helper function allows specifying the type parameter of {@link GunshiParamsConstraint}
319328
* while inferring the {@link Args} type, {@link ExtendContext} type from the definition.
320329
*
321330
* @example
@@ -340,7 +349,7 @@ type LazyWithTypesReturn<FullG extends GunshiParamsConstraint> = <
340349
* )
341350
* ```
342351
*
343-
* @typeParam G - A {@link GunshiParams} type
352+
* @typeParam G - A {@link GunshiParamsConstraint} type
344353
*
345354
* @returns A function that takes a lazy command definition via {@link lazy}
346355
*

0 commit comments

Comments
 (0)