Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prevSubject: 'element' is disallowed by current types #24416

Closed
jamesarosen opened this issue Oct 26, 2022 · 4 comments · Fixed by cypress-io/cypress-documentation#4954
Closed

Comments

@jamesarosen
Copy link

jamesarosen commented Oct 26, 2022

Current behavior

The TypeScript guide gives this example:

Cypress.Commands.add(
  'typeRandomWords',
  { prevSubject: 'element' },
  (subject /* :JQuery<HTMLElement> */, count = 3, options?) => {
    return cy.wrap(subject).type(generateRandomWords(count), options)
  }
)

That gives the following TypeScript error:

(property) prevSubject: false | ((keyof Cypress.PrevSubjectMap<unknown>)[] & false)
No overload matches this call.
  The last overload gave the following error.
    Type '"element"' is not assignable to type '(boolean | keyof PrevSubjectMap<unknown> | (keyof PrevSubjectMap<unknown>)[]) & (keyof PrevSubjectMap<unknown>)[]'.ts(2769)
cypress.d.ts(26, 5): The expected type comes from property 'prevSubject' which is declared here on type 'CommandOptions & { prevSubject: (keyof PrevSubjectMap<unknown>)[]; }'
cypress.d.ts(475, 7): The last overload is declared here.

The available types for add are

Commands: {
  add<T extends keyof Chainable>(name: T, fn: CommandFn<T>): void
  add<T extends keyof Chainable>(name: T, options: CommandOptions & {prevSubject: false}, fn: CommandFn<T>): void
  add<T extends keyof Chainable, S = any>(name: T, options: CommandOptions & {prevSubject: true}, fn: CommandFnWithSubject<T, S>): void
  add<T extends keyof Chainable, S extends PrevSubject>(
      name: T, options: CommandOptions & { prevSubject: S | ['optional'] }, fn: CommandFnWithSubject<T, PrevSubjectMap[S]>,
  ): void
  add<T extends keyof Chainable, S extends PrevSubject>(
      name: T, options: CommandOptions & { prevSubject: S[] }, fn: CommandFnWithSubject<T, PrevSubjectMap<void>[S]>,
  ): void
  addAll<T extends keyof Chainable>(fns: CommandFns): void
  addAll<T extends keyof Chainable>(options: CommandOptions & {prevSubject: false}, fns: CommandFns): void
  addAll<T extends keyof Chainable, S = any>(options: CommandOptions & { prevSubject: true }, fns: CommandFnsWithSubject<S>): void
  addAll<T extends keyof Chainable, S extends PrevSubject>(
      options: CommandOptions & { prevSubject: S | ['optional'] }, fns: CommandFnsWithSubject<PrevSubjectMap[S]>,
  ): void
  addAll<T extends keyof Chainable, S extends PrevSubject>(
      options: CommandOptions & { prevSubject: S[] }, fns: CommandFnsWithSubject<PrevSubjectMap<void>[S]>,
  ): void
  overwrite<T extends keyof Chainable>(name: T, fn: CommandFnWithOriginalFn<T>): void
  overwrite<T extends keyof Chainable, S extends PrevSubject>(name: T, fn: CommandFnWithOriginalFnAndSubject<T, PrevSubjectMap[S]>): void
}

Of those, every use of CommandOptions includes the & intersection operator to further limit prevSubject and none of them allows 'element'.

Desired behavior

Either (a) 'element' is an allowed prevSubject or the guide reflects the correct usage.

Test code to reproduce

See https://docs.cypress.io/guides/tooling/typescript-support#Types-for-Custom-Commands

Cypress Version

10.9.0

Node version

v16.17.1

Operating System

macOS 12.6

Debug Logs

N/A

Other

Related: #18940, #20377, #21106

@jamesarosen
Copy link
Author

This is the closest I could get to the code in the guide while making TypeScript happy:

// cypress/support/index.d.ts
typeRandomWords(count?: number, options?: Partial<TypeOptions>): Chainable<JQuery<HTMLElement>>

// cypress/support/commands.ts
Cypress.Commands.add(
  'typeRandomWords',
  { prevSubject: ['element'] },
  (subject /* :JQuery<HTMLElement> */, count = 3, options?) => {
    return cy.wrap(subject).type(generateRandomWords(count), options)
  }
)

@amehta265
Copy link
Contributor

@jamesarosen Thank you for opening this issue as well as providing a work around to this issue. I have been able to replicate this on my end and will be investigating this further.

@A-Shleifman
Copy link

A-Shleifman commented Dec 28, 2022

According to the internal interface PrevSubjectMap, commands with { prevSubject: ['element'] } should be returning JQueryWithSelector. Many internal commands return JQuery<HTMLElement> instead.

This makes TS happy:

declare global {
  namespace Cypress {
    interface Chainable {
      typeRandomWords(count?: number, options?: Partial<TypeOptions>): Chainable<JQueryWithSelector>;
    }
  }
}

Cypress.Commands.add('typeRandomWords', { prevSubject: 'element' }, (subject, count = 3, options?) => {
  return cy.wrap(subject).type(generateRandomWords(count), options);
});

The types look ok, but the docs need to be updated with the correct return type.

@emilyrohrbough
Copy link
Member

@A-Shleifman would you be interested in contributing the updates?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants