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

fix(types): fixed Fixtures type to disambiguite between intersected mapped types #33782

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

Andarist
Copy link
Contributor

@Andarist Andarist commented Nov 27, 2024

fixes #33763
context behind the change can be found here: microsoft/TypeScript#60619 (comment)
cc @dgozman

This comment has been minimized.

@Andarist
Copy link
Contributor Author

I ran a different set of tests locally and missed this before opening the PR. I'll look into the reported failure soon. FWIW, the failures are related to this piece of code:

const chain2 = chain1.extend<{ pageAsUser: Page }>({
pageAsUser: async ({ page }, use) => {
// @ts-expect-error
const x: number = page;
// @ts-expect-error
await use(x);
},
});

@dgozman
Copy link
Contributor

dgozman commented Nov 27, 2024

Thank you for the PR, this looks fantastic!

I ran a different set of tests locally and missed this before opening the PR. I'll look into the reported failure soon. FWIW, the failures are related to this piece of code:

I applied your suggestion to replace KeyValue with {} and that makes this test pass locally for me. Let's see what the bots think: #33784. Also works in this playground.

@Andarist
Copy link
Contributor Author

Andarist commented Nov 27, 2024

@dgozman I already included the constraint fix here too :) not in as many places though, I chose to be conservative with my change

Copy link
Contributor

Test results for "tests 1"

9 failed
❌ [installation tests] › playwright-test-plugin.spec.ts:35:5 › npm: @playwright/test plugin should work @package-installations-macos-latest
❌ [installation tests] › playwright-test-plugin.spec.ts:49:5 › pnpm: @playwright/test plugin should work @package-installations-macos-latest
❌ [installation tests] › playwright-test-plugin.spec.ts:63:5 › yarn: @playwright/test plugin should work @package-installations-macos-latest
❌ [installation tests] › playwright-test-plugin.spec.ts:35:5 › npm: @playwright/test plugin should work @package-installations-ubuntu-latest
❌ [installation tests] › playwright-test-plugin.spec.ts:49:5 › pnpm: @playwright/test plugin should work @package-installations-ubuntu-latest
❌ [installation tests] › playwright-test-plugin.spec.ts:63:5 › yarn: @playwright/test plugin should work @package-installations-ubuntu-latest
❌ [installation tests] › playwright-test-plugin.spec.ts:35:5 › npm: @playwright/test plugin should work @package-installations-windows-latest
❌ [installation tests] › playwright-test-plugin.spec.ts:49:5 › pnpm: @playwright/test plugin should work @package-installations-windows-latest
❌ [installation tests] › playwright-test-plugin.spec.ts:63:5 › yarn: @playwright/test plugin should work @package-installations-windows-latest

2 flaky ⚠️ [webkit-library] › library/trace-viewer.spec.ts:1512:1 › should serve css without content-type @webkit-ubuntu-22.04-node18
⚠️ [playwright-test] › ui-mode-test-watch.spec.ts:145:5 › should watch all @windows-latest-node18-1

37163 passed, 650 skipped
✔️✔️✔️

Merge workflow run.

@jakebailey
Copy link
Member

Per the linked issue, this only works if downstream users use TS 5.6+, right?

@Andarist
Copy link
Contributor Author

Per the linked issue, this only works if downstream users use TS 5.6+, right?

Yes. I don't think there is a suitable way to rewrite those types that would support older TS versions. typesVersions exist for a reason - but, I won't lie, they are not exactly easy to set up.

One idea to rewrite this would be to avoid reverse mapped type inference here somehow while preserving the contextual typing capabilities. Having that inference here isn't particularly desired and it doesn't quite work today anyway in this case.

@Andarist
Copy link
Contributor Author

I came up with this beautiful hack that makes it work: TS playground. Further testing would be required to make sure that it doesn't break anything but it looks really promising to me.

The idea behind this workaround is that properties of remapping mapped types don't contribute to contextual typing. This matches the pre-5.7 behavior for this particular scenario. So by ensuring that our as clause remaps the property by adding a dummy symbol in its "output" we make it works again. Given this symbol is completely private, I think it won't cause much harm... although it could impact type portability for the declaration emit. That could likely be addressed by Omiting it in the returned type if it even gets inferred as T's property (in the playground above it doesn't so this portability argument might just not be relevant to this situation after al).

@dgozman
Copy link
Contributor

dgozman commented Nov 28, 2024

@Andarist Let me play a bit with this beautiful hack 😄

That said, we have to support at least TypeScript 5.0, so I'd prefer to avoid typesVersions if possible. Given that, I've replaced keyof T as Exclude<...> with Exclude<keyof T, ...> in hope that would somewhat work in earlier TS versions. IIUC, that would not infer as good, but at least it may prevent any or wrong type being taken for overridden fixtures. Let's see how #33784 fares on the bots.

@Andarist
Copy link
Contributor Author

That said, we have to support at least TypeScript 5.0, so I'd prefer to avoid typesVersions if possible.

I mean, typesVersions is how you can support many different TS versions by providing different typing files for each of them. So that's exactly what could be used here if we don't find a suitable workaround. But also, I don't really recommend it because it would raise the complexity of your setup considerably ;p

Given that, I've replaced keyof T as Exclude<...> with Exclude<keyof T, ...> in hope that would somewhat work in earlier TS versions. IIUC, that would not infer as good, but at least it may prevent any or wrong type being taken for overridden fixtures.

It turns out that I confused myself here altogether. Both keyof T as Exclude<...> and Exclude<keyof T, ...> should do the trick for TS 5.0+.

It's correct to say that keyof T as Exclude<...> only works since TS 5.6 but that's when that mapped type is not intersected with other types.

All of this works based on subtle fact that you have this mix of known/resolved properties with properties of a type that is still generic. In such an intersection case, TS (before 5.7) would just ignore the generic mapped part of the intersection when providing a contextual type and would only use the more "concrete" part of the type. This wasn't consistent because in non-intersection cases it wouldn't ignore the generic mapped part at all, it would use it.

Fixing that had an unfortunate effect of breaking your types. But at the same time, it means that it doesn't quite matter how you write those generic mapped types here... because pre TS 5.7 they will just be ignored when selecting the contextual type.

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 this pull request may close these issues.

[Bug]: Types for fixtures are broken in TypeScript 5.7.2
3 participants