-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Declaration merging can break equality tests for named tuples #61162
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
Comments
Not a TS team member. Looks like the TS team isn't really interested in supporting that Two types that pass the |
While I'm sympathetic to that point of view, the issue with the other mutual assignability test defined as
When a type testing library is involved I'd say you probably don't want any of those to count as equal. If you want to deal with them what you'll need to do is do a recursive walk of the type (properly bailing on loops in the type!) and looking for these edge case types and then special casing them. While not impossible they're definitely not performant either. |
So then maybe we want upvotes and feedback on #48100 asking for a better type level equality operator, since neither mutual assignability nor reference equality does the trick, and user-defined versions are complex and slow. Maybe an argument could be made there that in the absence of a reasonable native operator, too many people are choosing to use an unsupported/unsupportable approach. I thought the main problem is that there are too many edge cases where people disagree on what equality is. |
This issue has been marked as "Not a Defect" and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
The only way to frame this issue to have it taken seriously is to show how it breaks assignability, not equality. Any mention of "equality" will seriously decrease the chances of a fix (case in point: this issue was closed without even being looked at simply because you referenced the equals hack as motivation). General principle: any type that is copy-and-pasted from one place to another (with the exception of So how does this issue manifest in a breakage of assignability? We can reframe it as follows: declare global { interface Array<T>{ m<O>(other: O extends T ? O : any): [T extends O ? any : any] }}
let a: <T>() => T extends [param?: string] ? 0 : 1 = null!;
let b: <T>() => T extends [param?: string] ? 0 : 1 = null!;
a = b; // ❌ Error!
Expected outcome: these two types should be assignable to each other, because they are syntactically identical and don't contain any Actual outcome: the compiler incorrectly complains that the two types are unrelated. I'd recommend opening a new issue without using the word "equals" anywhere, if you want this fixed. |
I raised On the contrary I've seen plenty of issues that mention Besides saying that assigning I understand that the TypeScript team is not willing to chase a definition of Of course I do disagree with the issue being closed. I believe the practical reality is that Now I do think something should be done, after all the performance of an alternative |
@LukeAbby It's not slight-of-hand, it's proof that an issue exists in its own right with non-internal implementation details, independently of this particular "equals" hack. Which is not obvious to a maintainer if the issue is framed only in terms of "equals" and does not demonstrate why it causes an assignability problem. But if you don't care about it anymore, that's fine too 🤷♂ Aside: it's funny that you should mention #57918 since the "equals" issue I mentioned there was fixed and merged after I reframed it in terms of assignability (see #61098). |
My point was that since assignability and extends are two sides of the same coin, I think I'd get the same reaction. To engage in a hypothetical, if I say that I'm surprised that declare let x: {};
declare let y: { x?: string };
x = y; // Ok Because I'm really saying the same thing as that's the whole point of the For example you'd see that this works: declare global { interface Array<T>{ m<O>(other: O extends T ? O : any): [T extends O ? any : any] }}
type NamedTuple = [param?: string]
let a: <T>() => T extends NamedTuple ? 0 : 1 = null!;
let b: <T>() => T extends NamedTuple ? 0 : 1 = null!;
a = b; // Ok And this functions the same with extends. Ultimately this is because the internal implementation that this uses can fall back to type id equality. Both snippets expose the |
I noticed the edit after posting. I considered making an edit too but I don't feel it diminishes my point. Nowhere did RyanCavanaugh say "we won't look at a PR for this." The original issue is still open. I consider this issue being closed to mean that they, at a minimum, won't consider implementing a PR themselves but may look at a PR if I open it. |
No guarantees, since it's not in the Backlog milestone. Experiment PRs that try to demonstrate something can be done with no ill effects are useful evidence, but we would like changes to the language to be motivated by use cases, not availability of code edits. |
While in the trivial case of two types that are textually equivalent this seems like an obvious case for a "bug" label, in general the assumption is that any two types are unrelated, and assignability has to be proven by some known path. It is always going to be possible to construct an assignment that "should" pass but fails, the question in practice is simply what other code are you willing to break (via introducing new circularity errors) or slow down (by doing new checks that don't go anywhere) or both (in the case of touching |
🔎 Search Terms
named tuples, equal, unequal
🕗 Version & Regression Information
This happens in
typescript@next
,typescript@latest
, and all the way back to[email protected]
(the earliest version that has tuples in the playground).⏯ Playground Link
https://www.typescriptlang.org/play/?#code/PTAEBUAsEsGdQKYEcCuBDANtALgT1AMaQIEDWochA9gLY1UB2G+KsCAJhQ6FgEYBOaftASwAdKACS2AOTxWHULxJoFFWfDShY2YQWwJ+2hAzagqAM0SpMOfNkhpuNFNnQZQaWLGgBzBmi80Fh4YgBQIFKgAA6GsIyYzKCC3NAM2FTqoA6UaZ7JCNGZrGm+oAAGAG44otjl4XixoACiNhiwADzgADSgAKoAfKAAvGGgoAAUHQBqAxMAlCND04gAHgYM7PDgoAD82fwoCKAAXKAWmGyLCOsmW5Mzc4vDy2sb9317B0en55cI8zG4y+uiOQPGZwu7QQAG4wmFGsdwLU+gwAjQOOAUNEMKIRi02p0ANo6YQMXwAXV6JN0pQpAzhkXGAD1vggImAoLl4PwEFhAklQQhwpEufBKFhSHz8MoCKozFo3DiEHJQOjjpQnBVJOwTNhoBYRPxyp5NvlokJsOYrA5jnKiMcpbgRWAAEIqNS2hW80AoNFoDGcW34aL8KixfhJXwIK0OKgoXyQK2WTzi3XpaByjASCZQJykeAAQU2QjgVosVCMOXghs2AEJAQjcE0APLRfUJDBY5X4okWwQ0XZnUl0uFNprInTgZsIQv82BtjsBLvY3HwYYE9ydRfQTvd3G9Hd71cIBkc4GsoXnsUUHl86AC+yHWHZYjZGcUe5CY6YADuaFweBkHccJxyRWp9zxDdWi3Do+yEANh1pckqVAeCByQslKTPJlQFZKE2HPABlSB4wwThlAqIV6nhREIFqI9l0g9dN0uOD+wDIdtGQylqQ4wdMLpHCwBZP5oWI0iUHIpRjnKajQJuIp+CtABvABfMddQIDBv1AXwMCoXhMFAFTwS4Ax+AuAhjkLfhBFwLohlM4EXNAUU33VeB2EyBgqCtGg0GwCzwlc4FIniDEAFkY1I9gOhbOY-OIfgzmadZBH0eLenAAZ5jOIk0t0NBMp6UAEopGE3LAIj-2iaJSnMVxzkrV9KACqV4EYJJwyXYylTXagaH7Y4-WAxJnTMlyIoQaK4zihKJiSwwzhbN47m2L5VrOJxcDytCdhud54FW-Ydt+HaKqqiAYHgdq8V4JLzHbXdl1NThfIYABaHqXr6k94AIWhht9BgxowZgQuBNSwjUoA
💻 Code
🙁 Actual behavior
TestTuples
andTestOptionalTuples
arefalse
whereasTestTypeAliasOptionalTuples
andTestUnnamedTuples
are true.🙂 Expected behavior
For all of these tests to return true.
Additional information about the issue
While this
someMethod
merge is obviously contrived, I actually ran into this while typing a library that adds anArray.equals
andArray.partition
method.After hunting it down I simplified the types to find this case.
The text was updated successfully, but these errors were encountered: