Skip to content

Commit cd5d2a9

Browse files
committed
fix: Add more precise types to Cypress.Commands
1 parent fc6131c commit cd5d2a9

File tree

2 files changed

+66
-9
lines changed

2 files changed

+66
-9
lines changed

cli/types/cypress.d.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,29 @@ declare namespace Cypress {
77
type HttpMethod = string
88
type RequestBody = string | object
99
type ViewportOrientation = 'portrait' | 'landscape'
10-
type PrevSubject = 'optional' | 'element' | 'document' | 'window'
10+
type PrevSubject = keyof PrevSubjectMap
1111
type TestingType = 'e2e' | 'component'
1212
type PluginConfig = (on: PluginEvents, config: PluginConfigOptions) => void | ConfigOptions | Promise<ConfigOptions>
1313

14+
interface PrevSubjectMap {
15+
optional: unknown
16+
element: JQuery
17+
document: JQuery<HTMLDocument>
18+
window: Window
19+
}
20+
1421
interface CommandOptions {
1522
prevSubject: boolean | PrevSubject | PrevSubject[]
1623
}
24+
interface CommandFn<T extends keyof ChainableMethods> {
25+
(...args: Parameters<ChainableMethods[T]>): ReturnType<ChainableMethods[T]> | void
26+
}
27+
interface CommandFnWithSubject<T extends keyof ChainableMethods, S = JQuery> {
28+
(prevSubject: S, ...args: Parameters<ChainableMethods[T]>): ReturnType<ChainableMethods[T]> | void
29+
}
30+
interface CommandFnWithOriginalFn<T extends keyof Chainable> {
31+
(originalFn: ChainableMethods[T], ...args: Parameters<ChainableMethods[T]>): ReturnType<ChainableMethods[T]> | void
32+
}
1733
interface ObjectLike {
1834
[key: string]: any
1935
}
@@ -420,9 +436,11 @@ declare namespace Cypress {
420436
* @see https://on.cypress.io/api/commands
421437
*/
422438
Commands: {
423-
add<T extends keyof Chainable>(name: T, fn: Chainable[T]): void
424-
add<T extends keyof Chainable>(name: T, options: CommandOptions, fn: Chainable[T]): void
425-
overwrite<T extends keyof Chainable>(name: T, fn: Chainable[T]): void
439+
add<T extends keyof Chainable>(name: T, fn: CommandFn<T>): void
440+
add<T extends keyof Chainable, S extends PrevSubject>(name: T, options: { prevSubject: S | Array<Exclude<S, 'optional'>> }, fn: CommandFnWithSubject<T, PrevSubjectMap[S]>): void
441+
add<T extends keyof Chainable, S extends PrevSubject>(name: T, options: { prevSubject: 'optional' | ['optional', ...S[]] }, fn: CommandFnWithSubject<T, PrevSubjectMap[S] | void>): void
442+
add<T extends keyof Chainable, S>(name: T, options: CommandOptions, fn: CommandFnWithSubject<T, S>): void
443+
overwrite<T extends keyof Chainable>(name: T, fn: CommandFnWithOriginalFn<T>): void
426444
}
427445

428446
/**
@@ -2255,6 +2273,12 @@ declare namespace Cypress {
22552273
$$<TElement extends Element = HTMLElement>(selector: JQuery.Selector, context?: Element | Document | JQuery): JQuery<TElement>
22562274
}
22572275

2276+
type ChainableMethods<Subject = any> = {
2277+
[P in keyof Chainable<Subject>]: Chainable<Subject>[P] extends ((...args: any[]) => any)
2278+
? Chainable<Subject>[P]
2279+
: never
2280+
}
2281+
22582282
interface SinonSpyAgent<A extends sinon.SinonSpy> {
22592283
log(shouldOutput?: boolean): Omit<A, 'withArgs'> & Agent<A>
22602284

cli/types/tests/cypress-tests.ts

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,20 +74,53 @@ namespace CypressCommandsTests {
7474
arg
7575
return
7676
})
77-
Cypress.Commands.add('newCommand', { prevSubject: true }, (arg) => {
77+
Cypress.Commands.add('newCommand', (arg) => {
7878
// $ExpectType string
7979
arg
80+
})
81+
Cypress.Commands.add('newCommand', { prevSubject: true }, (subject, arg) => {
82+
subject // $ExpectType unknown
83+
arg // $ExpectType string
84+
return
85+
})
86+
Cypress.Commands.add('newCommand', { prevSubject: 'optional' }, (subject, arg) => {
87+
subject // $ExpectType unknown
88+
arg // $ExpectType string
8089
return
8190
})
91+
Cypress.Commands.add('newCommand', { prevSubject: 'optional' }, (subject, arg) => {
92+
subject // $ExpectType unknown
93+
arg // $ExpectType string
94+
})
95+
Cypress.Commands.add('newCommand', { prevSubject: 'document' }, (subject, arg) => {
96+
subject // $ExpectType JQuery<HTMLDocument>
97+
arg // $ExpectType string
98+
})
99+
Cypress.Commands.add('newCommand', { prevSubject: 'window' }, (subject, arg) => {
100+
subject // $ExpectType Window
101+
arg // $ExpectType string
102+
})
103+
Cypress.Commands.add('newCommand', { prevSubject: 'element' }, (subject, arg) => {
104+
subject // $ExpectType JQuery<HTMLElement>
105+
arg // $ExpectType string
106+
})
107+
Cypress.Commands.add('newCommand', { prevSubject: ['element', 'document', 'window'] }, (subject, arg) => {
108+
subject // $ExpectType Window | JQuery<HTMLElement> | JQuery<HTMLDocument>
109+
arg // $ExpectType string
110+
})
111+
Cypress.Commands.add('newCommand', { prevSubject: ['optional', 'window', 'document', 'element'] }, (subject, arg) => {
112+
subject // $ExpectType void | Window | JQuery<HTMLElement> | JQuery<HTMLDocument>
113+
arg // $ExpectType string
114+
})
82115
Cypress.Commands.add('newCommand', (arg) => {
83116
// $ExpectType string
84117
arg
85118
return new Promise((resolve) => {})
86119
})
87-
Cypress.Commands.overwrite('newCommand', (arg) => {
88-
// $ExpectType string
89-
arg
90-
return
120+
Cypress.Commands.overwrite('newCommand', (originalFn, arg) => {
121+
arg // $ExpectType string
122+
originalFn // $ExpectedType Chainable['newCommand']
123+
originalFn(arg) // $ExpectType void
91124
})
92125
}
93126

0 commit comments

Comments
 (0)