User-event: [Typings] improve userEvent.type typings to only return a Promise when using delay is set

Created on 3 Nov 2020  路  6Comments  路  Source: testing-library/user-event

userEvent.type always returns a promise, but we only need to await it if we're using the { delay: number } as the third argument, according to the docs

// this returns a Promise, although it is not used
userEvent.type(getByRole('button'))

In js that's fine, but in TS, the compiler or eslint might warn you about having floating promises.

that could force the TS users to always use await even though it might not be necessary - it's different from js where we could just not await it.

A possible solution could be to use type overloading when defining the function type, so it returns Promise<void> if { delay: number } is passed, but no value is provided, return void

I think that could be achieved with the following code

type TypeWithoutDelay = (element: TargetElement, text: string, userOpts?: Omit<ITypeOpts, 'delay'> ) => void
type TypeWithDelay = (element: TargetElement, text: string, userOpts: Omit<ITypeOpts, 'delay'> & { delay: number }) => Promise<void>
// then on userEvent.type
declare const userEvent: {
  //...
  type: TypeWithDelay & TypeWithoutDelay
  //...
}

That should allow the inference to work

skipping options
image

not passing delay
image
image

using delay: number (which gives us the Promise as expected)
image
image

While this works, I wanted to read your thoughts as the code _does_ return a Promise (instead of void), just that it is not required to be awaited, so it could be misleading, or perhaps it's not worth the effort. I could do the PR as it is just as simple as updating the typings with that option.

enhancement

Most helpful comment

I have committed @ljosberinn's types from https://github.com/testing-library/user-event/issues/480#issuecomment-721288992 and added tests in #491.

All 6 comments

Could be a bit simpler I think:

https://www.typescriptlang.org/play?#code/AQKA9GwCIKYGYEsB2CAuCD2SDOwBGAngFzADqArsABICGG6NSwAPABaqoAO2REA5mlbk8AOgDGGALZgA7u2wBrAgD4QMAB6cMAJ1TBkqGNrg0xMYAEkAKgU4wA8p1S4A3iGDBFCTgGEANghiCgD8JHgYGH4wjO6eCt4AguSoGP4Y2DCh+BFRMR4AJjB+NARZSOSSeEaxyGgINH4AykUwYuhYjag0umUVVdo1KAxNLW2YSACiSPm9ldUAviBqmjp6BkYmZpZWNHgAqhnaju04wG4e2KwIcKhZ4ZHRSLFwGGLk2FbaNJxZUK8VMCQegAPsAJlFJIDUCBFsstLpgKhbOYdto+DBUOCYJCgcAALxgiFQ4Cg0jIfIYGRLDTwvRIuzAABiCCi2ASaIBuIJzKiJKZLJgAG0ALrUlYI+nmPacPwYGj5CxDdl8Tl6AnnYBiAJBRVoLIAWQw7xgEwAblDddCPGJWIx0ZasmaoTCxbT9ECNqZzBZ-IEFMdxq5Yl5OFQMObtHcco9Ylq-T4jUDZv0XSBClruuYJDg9MbtE6gSQNVrotoSAAKFo41AkVHozFEoEASnxymApowCHyse1CgrsQ8Vahte69ax1YANAP3XqSIbjQXUJapx4PBgnIGsj7ewGsNgV8AW3i2x2u7F8ng-L6gv3V8Ah4XgHWMeOoQePLVbnOjRlF8vp+uJzYFu17+hue4HkeJ6dt2FyjKgu44Leq4PjWT6ji+jaoO+7YNOQMA8J4qDaMgfB8tgxGkSKfJUFY+oADKvrioK0QxTGoCKOGfgaP4muaQL-h4UHtjB54EfBiGEeW06oSOaKYdib7TqaeEESQFEkUgZGghpVHCjRdGMVhBlsVhnHTtx34LvxS5DJBrYiWeHjkDKcr5Mhg5YXJY5YThiCsiQPIEcqqpcUMWTSrK8qWiFk6xMJp6wYiyIkMwVj3uohjTLg1jIsc2DKNJd6yeh8kNopQI4YYmXqZRWk4Xm+VZFY9nHk+GVZfkriFMUxDAOUczaPMwDBMAAAK2hSAgGTMIlygkIlsRdHgFaNU4wEkNYuwHEYkkJaJHicDQFEwB595eaVPkVdh07VWhun1RZ4VWb+NmCauR0nZJWQanen71CMURjB0XQ9CQA0pneH5DADzRAycUwzODfTVHe8ytdBTnAKw4ZGBWJXPuVk4zl+wDzq9FpDPtWPkEgOMRvjF2E+xE4kzx1mU2g1PdrC6bFNoWZ7nS4LeQp1YgHmi4iJK5YNqzADkLwYPLrMuD1JREAArAADLr8xNhLhxSzLcvAIrETy02QA

Could also go a little bit more complex to catch the case where delay: 0 is also synchronous, at the cost of showing something invalid for negative delays but a negative delay is technically nonsense:
https://www.typescriptlang.org/play?#code/FAehAIBEFMDMEsB28Au8D2iDO4BGBPALnAHUBXcACQEN01rFwAeACxRQActCwBzVFmVwA6AMboAtiADubLAGt8APmDQAHh3QAnFOCQpoW2NVHRwASQAq+DtADyHFDgDewcOAXwOAYQA28UXkAfmJcdHRfaAY3D3kvAEEyFHQ-dCxoELxwyOj3ABNoX2p8TMQyCVxDGKRUeGpfAGVC6FE0TAaUah1S8sqtauR6RubWjEQAUUQ8noqqgF9gVQ1tXRQbM0su3mgUcciJaERdAF5wPegDo-AAH1IkPPRpRYLRIq0zcWxdMnStcYA3Q4oYiudxrWzEJiWcDqAxTHBWdYOJxKAAUMXczUuwPAmy0212+yBABoMeADGocVgUFokLxSe53D9DMisJkAEpRB6IXz4KFKBngACU4GOSlxMMphzyLnABSKRHAAAZwAtGeAguB-uh4HkycRobDpbL5cViGVZlpVWT3JqAApaSTwdJMbW6lTq9zEN16hbAF5vD6YankvYGrY7c7Y4CfEPOgBqOryovAzL+gKOwnB0FRhOJ4AA5LBwgX8845YUzcrVUKY8HdM6HU70im0wCgVn1rm9vmiyWyxWFcQAIwqua12MNrDxXxYdCJ3Wt37tzPZ7u+XvF9AF2ugCBIcRad6tcnrcDvFBkLSMXBJcD1OfgRB0c9RXy8++MJAcO8oFjUXQDgYHAGHwP86SCOsvj0adZ3QJsJGdMxTjbDMUE7Wx103ftwHLU1FQAWlHMchSAA

oh lol delay is a number, not a boolean. My bad 馃槅 I guess it still applies the same idea

edit: I updated the PR details and title to reflect it is a number and not a boolean

@laquasicinque As clever as extends {delay: 0} is, it's unfortunately not very practical. Most users will be used to {delay: 0}, which is widened to the type {delay: number}, which will fail this check and return a Promise, which leads to the original issue here. While users could use {delay: 0 as const}, it's not very easy to learn, and I think going with a slightly more naive type is better than a confusing error message. Thanks for the idea though.

I have committed @ljosberinn's types from https://github.com/testing-library/user-event/issues/480#issuecomment-721288992 and added tests in #491.

Thanks, it looks great! 馃殌

Was this page helpful?
0 / 5 - 0 ratings