Uh oh!
There was an error while loading. Please reload this page.
add types for set methods proposal - #57230
Conversation
David Blass (ssalbdivad)
commented
Jan 30, 2024
I think this concept of "overlaps with" or its opposite "is disjoint with" is actually useful quite often. For example, it would be the ideal type of the parameter in exportconstincludes=<arrayextendsreadonlyunknown[],element>(array: array,element: element&array[number]extendsnever ? array[number] : element): element is element&array[number]=>array.includes(element)declareconstarray: string[]declareconstoverlapping: string|numberdeclareconstnonOverlapping: boolean|number// okayincludes(array,overlapping)// errorincludes(array,nonOverlapping)I wonder if the team would consider adding a builtin |
Bruce Pascoe (fatcerberus)
commented
Jan 30, 2024
I'm not sure there's a good solution for this right now - it's more or less the same problem as #48247/#26255 and the fix is generally considered to be #14520 (lower-bounded type parameters)--though FWIW I think an "overlaps with" constraint would be more useful for this than "supertype of" |
David Blass (ssalbdivad)
commented
Jan 30, 2024
Bruce Pascoe (@fatcerberus) I definitely agree declareconsta: {a: true}declareconstb: {b: true}// '{ a: true; }' and '{ b: true; }' have no overlapif(a===b){} |
Bruce Pascoe (fatcerberus)
commented
Jan 31, 2024
Well, in reality any |
David Blass (ssalbdivad)
commented
Jan 31, 2024
Bruce Pascoe (@fatcerberus) Maybe it would be an opportunity to revisit that logic if it were to be published as its own utility 😅 |
| ): Map<K, T[]>; | ||
| } | ||
| interface SetLike<T> { |
There was a problem hiding this comment.
At first glance, this looks a lot like ReadonlySet. I know that these methods only need a subset of functionality, but it might be worth using that instead.
There was a problem hiding this comment.
You need the keys method, so I don't think that works unless that gets added ReadonlySet as well. (And users shouldn't have to implement forEach for their own set-likes, though that isn't as big a deal since that probably doesn't come up as much.)
There was a problem hiding this comment.
Wait, no, keys is already there, just in a different file, whoops.
Still, I think it would be kind of annoying not to be able to do set.union({ keys(){ return array.keys() }, size: array.size, has: () => {} }) or similar. Not the end of the world, but I'd prefer to keep SetLike unless there's a reason to switch to ReadonlySet.
TypeScript Bot (@typescript-bot) pack this |
Heya Daniel Rosenwasser (@DanielRosenwasser), I've started to run the parallelized Definitely Typed test suite on this PR at d9efe96. You can monitor the build here. Update: The results are in! |
Heya Daniel Rosenwasser (@DanielRosenwasser), I've started to run the diff-based top-repos suite on this PR at d9efe96. You can monitor the build here. Update: The results are in! |
Heya Daniel Rosenwasser (@DanielRosenwasser), I've started to run the diff-based user code test suite on this PR at d9efe96. You can monitor the build here. Update: The results are in! |
Heya Daniel Rosenwasser (@DanielRosenwasser), I've started to run the tarball bundle task on this PR at d9efe96. You can monitor the build here. |
Heya Daniel Rosenwasser (@DanielRosenwasser), I've started to run the regular perf test suite on this PR at d9efe96. You can monitor the build here. Update: The results are in! |
Hey Daniel Rosenwasser (@DanielRosenwasser), I've packed this into an installable tgz. You can install it for testing by referencing it in your and then running There is also a playground for this build and an npm module you can use via |
The "overlaps" concept is what we internally call "comparability". The problem with expressing it as an intersection that produces In other words, you can't say that |
TypeScript Bot (typescript-bot)
commented
Jan 31, 2024
Daniel Rosenwasser (@DanielRosenwasser) Here are the results of running the user test suite comparing There were infrastructure failures potentially unrelated to your change:
Otherwise... Something interesting changed - please have a look. Details
|
TypeScript Bot (typescript-bot)
commented
Jan 31, 2024
Hey Daniel Rosenwasser (@DanielRosenwasser), the results of running the DT tests are ready. |
TypeScript Bot (typescript-bot)
commented
Jan 31, 2024
Daniel Rosenwasser (@DanielRosenwasser) Here they are:tscComparison Report - baseline..pr
System info unknown Hosts
Scenarios
tsserverComparison Report - baseline..pr
System info unknown Hosts
Scenarios
startupComparison Report - baseline..pr
System info unknown Hosts
Scenarios
Developer Information: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TypeScript Bot (typescript-bot)
commented
Jan 31, 2024
Daniel Rosenwasser (@DanielRosenwasser) Here are the results of running the top-repos suite comparing Everything looks good! |
David Blass (ssalbdivad)
commented
Jan 31, 2024
Daniel Rosenwasser (@DanielRosenwasser) Yes, that is exactly the behavior I'd expect in a structural type system, with a result of I'm often frustrated by TS overreaching based on a comparability check (e.g. #44645), especially when the word "overlap" is used in the error message which has a specific meaning that does not match the criteria for the error. |
I brought this up in the design meeting, specifically focusing on
The feedback for them was:
Kevin Gibbons (@bakkot), in this PR I'd suggest to just address the first two points, do whatever you feel is right for point 3, and we will bikeshed by the time we can bring this in for TypeScript 5.5. |
Kevin Gibbons (bakkot)
commented
Jan 31, 2024
WFM, thanks for the feedback. I'll get that pushed up later today. Any opinions on |
Uh oh!
There was an error while loading. Please reload this page.
I think the right thing for those is to take a |
Bruce Pascoe (fatcerberus)
commented
Feb 1, 2024
This is generally what people expect of e.g. |
Kevin Gibbons (@bakkot) I'm not from TS team and just curious: wouldn't it be better if typeReadonlySetLike<T>=Pick<Set<T>,'size'|'has'|'keys'>In any case of probable future changes of |
Kevin Gibbons (bakkot)
commented
Apr 15, 2024
Sure, that would work too. I leave it up to the TS to decide which approach they'd prefer. |
Doesn't TypeScript/src/lib/esnext.collection.d.ts Line 17 in 6ceb395 In current Chrome: constmySetLike={size: 2,has(a){returna==='foo'||a==='bar'},keys(){return['foo','bar']}};// new Set(['baz']).union(mySetLike);// Uncaught TypeError: string "next" is not a functionmySetLike.keys=()=>{leti=0;return{next(){i++;if(i===1)return{value: 'foo',done: false};if(i===2)return{value: 'bar',done: false};return{done: true};}};};newSet(['baz']).union(mySetLike);// Set(3) {'baz', 'foo', 'bar'}Piggybacking off |
Kevin Gibbons (bakkot)
commented
Apr 16, 2024
Malcolm (@noinkling) Nice catch, that's an embarrassing typo given that I designed the feature 😅. Fixed. Possibly should update the description of |
Kevin Gibbons (@bakkot) One more question about return type for difference<U>(otherSet: ReadonlySetLike<U>): Set<Exclude<T,U>>symmetricDifference<U>(otherSet: ReadonlySetLike<U>): Set<Exclude<T,U>&Exclude<U,T>> |
|
Any blockers* for this PR? My PR into Mobx mobxjs/mobx#3853 state manager (which mostly used for React.js) is depending on it * except failed tests |
Ron Buckton (rbuckton)
left a comment
There was a problem hiding this comment.
It looks like baselines need to be updated after the most recent commit.
Ron Buckton (rbuckton)
commented
May 16, 2024
Those definitions would be too strict since a given instantiation of constset1=newSet<"A"|"B">(["A","B"]);constset2=newSet<"B"|"C">(["C"]);constset3=set1.difference(set2);set.has("A");// trueset.has("B");// trueEven though |
Ron Buckton (rbuckton)
commented
May 16, 2024
IMO, an interface is a better option as it allows for interface merging if a polyfill is loaded that also introduces these types. |
Ron Buckton (@rbuckton) so maybe wrap it in |
| isSubsetOf(other: ReadonlySetLike<unknown>): boolean; | ||
| /** | ||
| * @returns a boolean indicating whether all the elements in the argument are also in this Set. | ||
| */ | ||
| isSupersetOf(other: ReadonlySetLike<unknown>): boolean; |
There was a problem hiding this comment.
Daniel Rosenwasser (@DanielRosenwasser) would overloads like the following be worthwhile?
interfaceSet<T>{isSubsetOf<U>(other: ReadonlySetLike<U>): this is Set<T&U>;isSupersetOf<U>(other: ReadonlySetLike<U>): other is ReadonlySetLike<T&U>;}There was a problem hiding this comment.
We can always change these in a follow-up PR.
Uh oh!
There was an error while loading. Please reload this page.
Ron Buckton (rbuckton)
commented
May 16, 2024
constset1=newSet<"A"|"B">(["A","B"]);constset2=set1.difference(["B","C"]);// infer the type `["B", "C"]` hereHowever, |
Kevin Gibbons (bakkot)
commented
May 16, 2024
Hm. The way I've been doing that ( |
Jake Bailey (jakebailey)
commented
May 16, 2024
CI always tests main + the PR; have you merged main locally? |
Kevin Gibbons (bakkot)
commented
May 16, 2024
Aha, thanks, that's fixed it. |
| isSubsetOf(other: ReadonlySetLike<unknown>): boolean; | ||
| /** | ||
| * @returns a boolean indicating whether all the elements in the argument are also in this Set. | ||
| */ | ||
| isSupersetOf(other: ReadonlySetLike<unknown>): boolean; |
There was a problem hiding this comment.
We can always change these in a follow-up PR.
Fixes#57228.
Set methods are stage 3, and I expect to ask for stage 4 in April.
I've used the simplest possible types here, but in some sense these are too restrictive. It's perfectly reasonable to ask if a Set of
0 | 1is a subset of a Set ofnumber, for example, or even of a Set of1 | 2- anything where the intersection of the types is nonempty (I guess that would be, any typeSsuch thatT & S extends neveris false).Unfortunately I don't know of a good way to express that constraint, at least not without making the types way more complex. So I've stuck with the simple thing here. But if someone has a suggestion for better types I'd happily take it.
One possibility would be to have the Set-producing types take
SetLike<T>but the predicates takeSetLike<unknown>. I don't know if that would be better.Do note that this this PR introduces the
SetLikeinterface.