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

Inconsistent Behavior with Equality Check Using Pick<Readonly<T>, K> in Mapped Type #58163

Closed
Snowflyt opened this issue Apr 12, 2024 · 2 comments
Labels
Not a Defect This behavior is one of several equally-correct options

Comments

@Snowflyt
Copy link

πŸ”Ž Search Terms

"readonly", "pick", "equality", "equal"

πŸ•— Version & Regression Information

This is the behavior in every version I tried, and I reviewed the FAQ for entries about Generics.

⏯ Playground Link

https://www.typescriptlang.org/play?ts=5.4.5#code/JYOwLgpgTgZghgYwgAgPLIN4ChnJge3wC5kBnMKUAcwG4sBfLLMATwAcUBRARwFc4ANgB4AGgBpkATQB8yALw5kACiEAVaUoCU82auQQAHpBAATUshHIA-MgCMyEgCZth42eRqN2ubv1GIpuaS1nYOyI4hFLwoJPACpBB0zOwoAFK85AAKwAgA1p7ymIoA2gDSyKDIuRAs+DDIqgC6JNl5ahKl0nT0dKwcyJDkAPoADIXpWTn5qNLFAEQE+HONdAD0q7jIAHpWJBh4hCTklCBUyIzJ-a25AEoQcCb4IAIsBXJFuGUVIFU1dQ3NZDXIR3B5PF6eDpdBi9FIDCDDezva6gx7PV4zeaLZZrDa4HZ7ZBQe5ol4HYhkCjUc5MPooQZgIYRd48fjCfaLI5U07nCT7Ylg9HkrknM70aHrTYEvCCBK0uEAZQocFOEDeH2QX0q1Vq9SaJFZgiEwNUUIkwNR4Ne6ih0Nwks2jqdzu2Wzd7vd+I93p9HoaAAtgOZSP78LwBCZkAAjFD8klW4WU0XnZCIBD4KAmalgfDIAAGKPj6LzigdLvLUt9btLeM2qkDwdD4cjMcwieO1PoqYQ6cz2dzeYmYGuJftGwAdJOyAPDcITWagVMQUWITbkJ1pHmyE2I9GUHm4gkt2mM1meTn8wymSXGHT4cMAMyFJVQFVUNWYhaEHFYMvSqIoEIAC0yAAOr+iwViXPSCKMgALIUs7GkuqASF+SzSOaS6WuiQgzGh2LSBKtbSoeEBYEAA

πŸ’» Code

interface O {
  foo: string;
}

type Equal<X, Y> =
  (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;

type JustPick<T> = {
  [K in keyof T]: Pick<T, K>;
};
type test_0 = JustPick<O>["foo"];
//   ^?: { foo: string }

type PickReadonly<T> = {
  [K in keyof T]: Pick<Readonly<T>, K>;
};
type test_1 = PickReadonly<O>["foo"];
//   ^?: { readonly foo: string }

type test_2 = Equal<{ foo: string }, { readonly foo: string }>;
//   ^?: false

type Strange<T> = {
  [K in keyof T]: Equal<Pick<T, K>, Pick<Readonly<T>, K>>;
  //                    ^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^ This should be { readonly foo: string } according to `PickReadonly`
  //                    ^^^^^^^^^^
  //     This should be { foo: string } according to `JustPick`
  // ... so `Equal<Pick<T, K>, Pick<Readonly<T>, K>>` should be `false` according to `test_2`
}
type test_3 = Strange<O>["foo"];
//   ^?: true <- Why?
type test_4 = Equal<Pick<O, "foo">, Pick<Readonly<O>, "foo">>;
//   ^?: false

πŸ™ Actual behavior

Type of test_3 is true instead of expected false like test_2 and test_4.

πŸ™‚ Expected behavior

Type of test_3 should be false like test_2 and test_4.

Additional information about the issue

No response

@RyanCavanaugh
Copy link
Member

This definition of Equal intentionally does the one thing that relies on the internal representation of types in the compiler. I really do not recommend using it. A definition that isn't reverse-engineered to actively rely on implementation details is

type Equal<X, Y> = [X, Y] extends [Y, X] ? true : false;

which gives consistent results

readonly doesn't affect assignability so can't really be used in conditional types like this. Attempts to manifest other behavior will ultimately fail.

@RyanCavanaugh RyanCavanaugh added the Not a Defect This behavior is one of several equally-correct options label Apr 12, 2024
@Snowflyt
Copy link
Author

This definition of Equal intentionally does the one thing that relies on the internal representation of types in the compiler. I really do not recommend using it. A definition that isn't reverse-engineered to actively rely on implementation details is

type Equal<X, Y> = [X, Y] extends [Y, X] ? true : false;

which gives consistent results

readonly doesn't affect assignability so can't really be used in conditional types like this. Attempts to manifest other behavior will ultimately fail.

Thanks for the detailed responseβ€”it really cleared things up for me! I appreciate your help and the alternative you provided.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Not a Defect This behavior is one of several equally-correct options
Projects
None yet
Development

No branches or pull requests

2 participants