testing-library / svelte-testing-library

:chipmunk: Simple and complete Svelte DOM testing utilities that encourage good testing practices
https://testing-library.com/docs/svelte-testing-library/intro
MIT License
620 stars 33 forks source link

How to wrap `render` function and pass along types to underlying `render` function #408

Closed crookedneighbor closed 2 weeks ago

crookedneighbor commented 2 weeks ago

Hey there, this is probably user error, but I'm confused about it.

I've created a simple wrapper function around render that sets up a user and returns it, along with all the props that render returns. It looks like this:

import userEvent, { type Options } from '@testing-library/user-event';
import { render } from '@testing-library/svelte';

export function setup(
  component: Parameters<typeof render>[0],
  renderOptions: Parameters<typeof render>[1] = {},
  additionalOptions?: {
    userEventOptions?: Options;
  }
) {
  return {
    user: userEvent.setup(additionalOptions?.userEventOptions),
    ...render(component, renderOptions),
  };
}

And it works, great! Except, VS Code complains about it.

So when calling render, it accurately interprets the prop types:

render(Component, {
  propThatDoesExist, // no TS error, yay!
  propThatDoesNotExist, // TS error, yay!
});

But if I use my helper function, it can't interpret the props correctly:

setup(Component, {
  propThatDoesExist // Object literal may only specify known properties, and 'propThatDoesExist' does not exist in type 'Partial<ComponentConstructorOptions<never>>'.ts(2353)
});

Am I missing something obvious? I thought that if I set the params to be exactly the same, it should be able to infer the types correctly, but maybe there's some TS magic I'm missing?

mcous commented 2 weeks ago

Oh, interesting! I think the never here is an important hint:

does not exist in type 'Partial<ComponentConstructorOptions<never>>'

render is generic on the component, which is showing up as never in that error. You need to make sure that your setup function is also generic:

export function setup<C>(
  component: Parameters<typeof render<C>>[0],
  renderOptions: Parameters<typeof render<C>>[1] = {},
  additionalOptions?: {
    userEventOptions?: Options;
  }
) {
  // ...
}
crookedneighbor commented 2 weeks ago

Awesome, yep, that works perfectly!