TypeScript
TypeScript copied to clipboard
add types for set methods proposal
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 | 1 is a subset of a Set of number, for example, or even of a Set of 1 | 2 - anything where the intersection of the types is nonempty (I guess that would be, any type S such that T & S extends never is 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 take SetLike<unknown>. I don't know if that would be better.
Do note that this this PR introduces the SetLike interface.
I guess that would be, any type S such that T & S extends never is false
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 [].includes(...). Currently I use this utility:
export const includes = <array extends readonly unknown[], element>(
array: array,
element: element & array[number] extends never ? array[number] : element
): element is element & array[number] => array.includes(element)
declare const array: string[]
declare const overlapping: string | number
declare const nonOverlapping: boolean | number
// okay
includes(array, overlapping)
// error
includes(array, nonOverlapping)
I wonder if the team would consider adding a builtin Overlaps type for cases like these and the set methods in this PR.
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.
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"
@fatcerberus I definitely agree overlaps would be very useful in a number of circumstances like includes, as well as internally for a lot of these comparison errors that incorrectly assert the lack of an overlap (when really they're just checking for a downcast):
declare const a: { a: true }
declare const b: { b: true }
// '{ a: true; }' and '{ b: true; }' have no overlap
if (a === b) {
}
Well, in reality any overlaps primitive we got would likely use the same internal check that produces the "these types don't overlap" error, so I don't think it would fix that problem by itself :wink:
@fatcerberus Maybe it would be an opportunity to revisit that logic if it were to be published as its own utility 😅
@typescript-bot pack this @typescript-bot test this @typescript-bot test top200 @typescript-bot user test this @typescript-bot run dt @typescript-bot perf test this
Heya @DanielRosenwasser, I've started to run the parallelized Definitely Typed test suite on this PR at d9efe9618eab17785c35964dd44f0699ac01c5f7. You can monitor the build here.
Update: The results are in!
Heya @DanielRosenwasser, I've started to run the diff-based top-repos suite on this PR at d9efe9618eab17785c35964dd44f0699ac01c5f7. You can monitor the build here.
Update: The results are in!
Heya @DanielRosenwasser, I've started to run the diff-based user code test suite on this PR at d9efe9618eab17785c35964dd44f0699ac01c5f7. You can monitor the build here.
Update: The results are in!
Heya @DanielRosenwasser, I've started to run the tarball bundle task on this PR at d9efe9618eab17785c35964dd44f0699ac01c5f7. You can monitor the build here.
Heya @DanielRosenwasser, I've started to run the regular perf test suite on this PR at d9efe9618eab17785c35964dd44f0699ac01c5f7. You can monitor the build here.
Update: The results are in!
Hey @DanielRosenwasser, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:
{
"devDependencies": {
"typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/159718/artifacts?artifactName=tgz&fileId=2BD1440F52B0CE1DB5E5082986F8EE22B10337C65AD4EDD247C29E5340509AFF02&fileName=/typescript-5.4.0-insiders.20240131.tgz"
}
}
and then running npm install.
There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/[email protected]".;
The "overlaps" concept is what we internally call "comparability". The problem with expressing it as an intersection that produces never is that it is rarely possible to say an intersection of two object types is vacuous.
In other words, you can't say that { meow(): void } & { woof(): void } is an impossible type to fulfill, so there is no straightforward way to disallow catSet.intersection(dogSet).
@DanielRosenwasser Here are the results of running the user test suite comparing main and refs/pull/57230/merge:
There were infrastructure failures potentially unrelated to your change:
- 1 instance of "Package install failed"
Otherwise...
Something interesting changed - please have a look.
Details
puppeteer
packages/browsers/test/src/tsconfig.json
- [MISSING]
error TS2307: Cannot find module '@pptr/testserver' or its corresponding type declarations.
Hey @DanielRosenwasser, the results of running the DT tests are ready. Everything looks the same! You can check the log here.
@DanielRosenwasser The results of the perf run you requested are in!
Here they are:
tsc
Comparison Report - baseline..pr| Metric | baseline | pr | Delta | Best | Worst | p-value |
|---|---|---|---|---|---|---|
| Angular - node (v18.15.0, x64) | ||||||
| Memory used | 295,662k (± 0.01%) | 295,671k (± 0.01%) | ~ | 295,637k | 295,725k | p=0.810 n=6 |
| Parse Time | 2.66s (± 0.31%) | 2.67s (± 0.19%) | +0.01s (+ 0.38%) | 2.66s | 2.67s | p=0.050 n=6 |
| Bind Time | 0.83s (± 1.52%) | 0.82s (± 0.63%) | ~ | 0.82s | 0.83s | p=0.418 n=6 |
| Check Time | 8.19s (± 0.47%) | 8.21s (± 0.34%) | ~ | 8.18s | 8.26s | p=0.870 n=6 |
| Emit Time | 7.10s (± 0.25%) | 7.10s (± 0.23%) | ~ | 7.08s | 7.12s | p=1.000 n=6 |
| Total Time | 18.78s (± 0.30%) | 18.80s (± 0.15%) | ~ | 18.78s | 18.86s | p=1.000 n=6 |
| Compiler-Unions - node (v18.15.0, x64) | ||||||
| Memory used | 193,514k (± 1.59%) | 192,944k (± 1.25%) | ~ | 191,491k | 197,365k | p=0.689 n=6 |
| Parse Time | 1.35s (± 0.87%) | 1.36s (± 1.76%) | ~ | 1.32s | 1.38s | p=0.220 n=6 |
| Bind Time | 0.72s (± 0.00%) | 0.72s (± 0.00%) | ~ | 0.72s | 0.72s | p=1.000 n=6 |
| Check Time | 9.38s (± 0.50%) | 9.41s (± 0.37%) | ~ | 9.36s | 9.44s | p=0.469 n=6 |
| Emit Time | 2.62s (± 0.66%) | 2.61s (± 1.40%) | ~ | 2.55s | 2.66s | p=0.570 n=6 |
| Total Time | 14.08s (± 0.38%) | 14.10s (± 0.37%) | ~ | 14.00s | 14.14s | p=0.332 n=6 |
| Monaco - node (v18.15.0, x64) | ||||||
| Memory used | 347,460k (± 0.00%) | 347,465k (± 0.00%) | ~ | 347,442k | 347,478k | p=0.336 n=6 |
| Parse Time | 2.48s (± 0.44%) | 2.48s (± 0.36%) | ~ | 2.47s | 2.49s | p=1.000 n=6 |
| Bind Time | 0.93s (± 0.68%) | 0.93s (± 0.44%) | ~ | 0.92s | 0.93s | p=0.673 n=6 |
| Check Time | 6.96s (± 0.56%) | 6.96s (± 0.55%) | ~ | 6.92s | 7.03s | p=1.000 n=6 |
| Emit Time | 4.07s (± 0.45%) | 4.06s (± 0.34%) | ~ | 4.04s | 4.08s | p=0.241 n=6 |
| Total Time | 14.44s (± 0.36%) | 14.43s (± 0.32%) | ~ | 14.38s | 14.50s | p=0.809 n=6 |
| TFS - node (v18.15.0, x64) | ||||||
| Memory used | 302,858k (± 0.01%) | 302,858k (± 0.01%) | ~ | 302,832k | 302,894k | p=0.689 n=6 |
| Parse Time | 2.01s (± 0.86%) | 2.02s (± 0.68%) | ~ | 1.99s | 2.03s | p=0.934 n=6 |
| Bind Time | 1.01s (± 1.04%) | 1.00s (± 1.22%) | ~ | 0.99s | 1.02s | p=1.000 n=6 |
| Check Time | 6.33s (± 0.41%) | 6.34s (± 0.34%) | ~ | 6.31s | 6.37s | p=0.624 n=6 |
| Emit Time | 3.59s (± 0.45%) | 3.59s (± 0.42%) | ~ | 3.57s | 3.61s | p=1.000 n=6 |
| Total Time | 12.94s (± 0.21%) | 12.96s (± 0.19%) | ~ | 12.92s | 12.98s | p=0.515 n=6 |
| material-ui - node (v18.15.0, x64) | ||||||
| Memory used | 511,313k (± 0.00%) | 511,325k (± 0.01%) | ~ | 511,290k | 511,384k | p=0.748 n=6 |
| Parse Time | 2.65s (± 0.66%) | 2.65s (± 0.98%) | ~ | 2.62s | 2.68s | p=0.935 n=6 |
| Bind Time | 0.99s (± 1.04%) | 1.00s (± 0.98%) | ~ | 0.99s | 1.01s | p=0.203 n=6 |
| Check Time | 17.27s (± 0.37%) | 17.28s (± 0.40%) | ~ | 17.19s | 17.37s | p=0.936 n=6 |
| Emit Time | 0.00s (± 0.00%) | 0.00s (± 0.00%) | ~ | 0.00s | 0.00s | p=1.000 n=6 |
| Total Time | 20.90s (± 0.26%) | 20.92s (± 0.33%) | ~ | 20.83s | 21.03s | p=0.685 n=6 |
| mui-docs - node (v18.15.0, x64) | ||||||
| Memory used | 1,695,135k (± 0.00%) | 1,695,118k (± 0.00%) | ~ | 1,695,074k | 1,695,186k | p=0.199 n=6 |
| Parse Time | 6.53s (± 0.29%) | 6.52s (± 0.38%) | ~ | 6.49s | 6.56s | p=0.466 n=6 |
| Bind Time | 2.36s (± 0.22%) | 2.36s (± 0.35%) | ~ | 2.34s | 2.36s | p=0.114 n=6 |
| Check Time | 55.43s (± 0.37%) | 55.49s (± 0.33%) | ~ | 55.24s | 55.66s | p=0.687 n=6 |
| Emit Time | 0.16s (± 2.52%) | 0.16s (± 2.52%) | ~ | 0.16s | 0.17s | p=1.000 n=6 |
| Total Time | 64.48s (± 0.34%) | 64.53s (± 0.30%) | ~ | 64.26s | 64.71s | p=0.810 n=6 |
| self-build-src - node (v18.15.0, x64) | ||||||
| Memory used | 2,410,763k (± 0.04%) | 2,410,667k (± 0.03%) | ~ | 2,409,469k | 2,411,451k | p=1.000 n=6 |
| Parse Time | 4.93s (± 0.99%) | 4.94s (± 0.59%) | ~ | 4.89s | 4.98s | p=0.575 n=6 |
| Bind Time | 1.88s (± 0.27%) | 1.88s (± 1.08%) | ~ | 1.87s | 1.92s | p=0.277 n=6 |
| Check Time | 33.45s (± 0.23%) | 33.46s (± 0.35%) | ~ | 33.26s | 33.58s | p=0.810 n=6 |
| Emit Time | 2.69s (± 1.90%) | 2.70s (± 1.91%) | ~ | 2.64s | 2.78s | p=0.810 n=6 |
| Total Time | 42.98s (± 0.17%) | 42.99s (± 0.30%) | ~ | 42.76s | 43.15s | p=0.335 n=6 |
| self-compiler - node (v18.15.0, x64) | ||||||
| Memory used | 418,598k (± 0.00%) | 418,659k (± 0.02%) | ~ | 418,588k | 418,780k | p=0.230 n=6 |
| Parse Time | 2.79s (± 2.23%) | 2.74s (± 3.78%) | ~ | 2.66s | 2.87s | p=0.569 n=6 |
| Bind Time | 1.10s (± 5.44%) | 1.17s (± 6.50%) | ~ | 1.07s | 1.23s | p=0.112 n=6 |
| Check Time | 15.10s (± 0.36%) | 15.10s (± 0.34%) | ~ | 15.03s | 15.16s | p=1.000 n=6 |
| Emit Time | 1.14s (± 1.11%) | 1.14s (± 1.29%) | ~ | 1.12s | 1.16s | p=0.869 n=6 |
| Total Time | 20.13s (± 0.27%) | 20.15s (± 0.34%) | ~ | 20.07s | 20.24s | p=0.575 n=6 |
| vscode - node (v18.15.0, x64) | ||||||
| Memory used | 2,811,213k (± 0.00%) | 2,811,228k (± 0.00%) | ~ | 2,811,192k | 2,811,278k | p=0.748 n=6 |
| Parse Time | 10.62s (± 0.38%) | 10.61s (± 0.37%) | ~ | 10.56s | 10.65s | p=0.808 n=6 |
| Bind Time | 3.42s (± 0.55%) | 3.41s (± 1.15%) | ~ | 3.39s | 3.49s | p=0.413 n=6 |
| Check Time | 59.81s (± 0.67%) | 60.09s (± 0.57%) | ~ | 59.62s | 60.54s | p=0.378 n=6 |
| Emit Time | 16.14s (± 0.57%) | 16.16s (± 0.62%) | ~ | 16.00s | 16.29s | p=0.628 n=6 |
| Total Time | 89.99s (± 0.38%) | 90.27s (± 0.43%) | ~ | 89.72s | 90.75s | p=0.230 n=6 |
| webpack - node (v18.15.0, x64) | ||||||
| Memory used | 393,456k (± 0.01%) | 393,471k (± 0.02%) | ~ | 393,354k | 393,639k | p=0.936 n=6 |
| Parse Time | 3.08s (± 0.65%) | 3.07s (± 0.74%) | ~ | 3.04s | 3.11s | p=0.492 n=6 |
| Bind Time | 1.41s (± 0.39%) | 1.39s (± 1.33%) | ~ | 1.37s | 1.41s | p=0.341 n=6 |
| Check Time | 14.00s (± 0.30%) | 14.03s (± 0.34%) | ~ | 13.94s | 14.06s | p=0.171 n=6 |
| Emit Time | 0.00s (± 0.00%) | 0.00s (±244.70%) | ~ | 0.00s | 0.01s | p=0.405 n=6 |
| Total Time | 18.48s (± 0.21%) | 18.49s (± 0.20%) | ~ | 18.43s | 18.52s | p=0.627 n=6 |
| xstate - node (v18.15.0, x64) | ||||||
| Memory used | 513,374k (± 0.01%) | 513,407k (± 0.00%) | ~ | 513,381k | 513,432k | p=0.065 n=6 |
| Parse Time | 3.27s (± 0.36%) | 3.28s (± 0.30%) | ~ | 3.27s | 3.29s | p=0.314 n=6 |
| Bind Time | 1.54s (± 0.49%) | 1.54s (± 0.49%) | ~ | 1.53s | 1.55s | p=1.000 n=6 |
| Check Time | 2.84s (± 0.87%) | 2.85s (± 0.91%) | ~ | 2.81s | 2.88s | p=0.685 n=6 |
| Emit Time | 0.08s (± 4.99%) | 0.08s (± 0.00%) | ~ | 0.08s | 0.08s | p=0.405 n=6 |
| Total Time | 7.73s (± 0.29%) | 7.74s (± 0.47%) | ~ | 7.70s | 7.79s | p=0.518 n=6 |
- node (v18.15.0, x64)
- Angular - node (v18.15.0, x64)
- Compiler-Unions - node (v18.15.0, x64)
- Monaco - node (v18.15.0, x64)
- TFS - node (v18.15.0, x64)
- material-ui - node (v18.15.0, x64)
- mui-docs - node (v18.15.0, x64)
- self-build-src - node (v18.15.0, x64)
- self-compiler - node (v18.15.0, x64)
- vscode - node (v18.15.0, x64)
- webpack - node (v18.15.0, x64)
- xstate - node (v18.15.0, x64)
| Benchmark | Name | Iterations |
|---|---|---|
| Current | pr | 6 |
| Baseline | baseline | 6 |
tsserver
Comparison Report - baseline..pr| Metric | baseline | pr | Delta | Best | Worst | p-value |
|---|---|---|---|---|---|---|
| Compiler-UnionsTSServer - node (v18.15.0, x64) | ||||||
| Req 1 - updateOpen | 2,345ms (± 0.43%) | 2,342ms (± 0.75%) | ~ | 2,330ms | 2,377ms | p=0.227 n=6 |
| Req 2 - geterr | 5,546ms (± 1.36%) | 5,548ms (± 1.54%) | ~ | 5,419ms | 5,620ms | p=0.936 n=6 |
| Req 3 - references | 323ms (± 0.30%) | 325ms (± 1.56%) | ~ | 321ms | 332ms | p=0.742 n=6 |
| Req 4 - navto | 274ms (± 1.45%) | 275ms (± 1.32%) | ~ | 271ms | 279ms | p=1.000 n=6 |
| Req 5 - completionInfo count | 1,356 (± 0.00%) | 1,356 (± 0.00%) | ~ | 1,356 | 1,356 | p=1.000 n=6 |
| Req 5 - completionInfo | 87ms (± 8.43%) | 90ms (± 7.40%) | ~ | 84ms | 101ms | p=0.466 n=6 |
| CompilerTSServer - node (v18.15.0, x64) | ||||||
| Req 1 - updateOpen | 2,479ms (± 1.56%) | 2,483ms (± 1.23%) | ~ | 2,448ms | 2,535ms | p=0.689 n=6 |
| Req 2 - geterr | 4,197ms (± 2.07%) | 4,181ms (± 1.78%) | ~ | 4,123ms | 4,279ms | p=0.630 n=6 |
| Req 3 - references | 336ms (± 1.77%) | 340ms (± 1.61%) | ~ | 331ms | 346ms | p=0.568 n=6 |
| Req 4 - navto | 284ms (± 0.53%) | 285ms (± 1.19%) | ~ | 281ms | 291ms | p=0.466 n=6 |
| Req 5 - completionInfo count | 1,518 (± 0.00%) | 1,518 (± 0.00%) | ~ | 1,518 | 1,518 | p=1.000 n=6 |
| Req 5 - completionInfo | 80ms (± 6.65%) | 85ms (± 8.23%) | ~ | 74ms | 90ms | p=0.106 n=6 |
| xstateTSServer - node (v18.15.0, x64) | ||||||
| Req 1 - updateOpen | 2,598ms (± 0.57%) | 2,612ms (± 0.44%) | ~ | 2,594ms | 2,626ms | p=0.126 n=6 |
| Req 2 - geterr | 1,736ms (± 2.23%) | 1,731ms (± 2.70%) | ~ | 1,680ms | 1,783ms | p=1.000 n=6 |
| Req 3 - references | 116ms (± 9.63%) | 119ms (± 8.24%) | ~ | 107ms | 128ms | p=0.466 n=6 |
| Req 4 - navto | 370ms (± 0.41%) | 371ms (± 0.56%) | ~ | 369ms | 375ms | p=1.000 n=6 |
| Req 5 - completionInfo count | 2,078 (± 0.00%) | 2,078 (± 0.00%) | ~ | 2,078 | 2,078 | p=1.000 n=6 |
| Req 5 - completionInfo | 311ms (± 1.57%) | 311ms (± 2.40%) | ~ | 301ms | 319ms | p=0.873 n=6 |
- node (v18.15.0, x64)
- CompilerTSServer - node (v18.15.0, x64)
- Compiler-UnionsTSServer - node (v18.15.0, x64)
- xstateTSServer - node (v18.15.0, x64)
| Benchmark | Name | Iterations |
|---|---|---|
| Current | pr | 6 |
| Baseline | baseline | 6 |
startup
Comparison Report - baseline..pr| Metric | baseline | pr | Delta | Best | Worst | p-value |
|---|---|---|---|---|---|---|
| tsc-startup - node (v18.15.0, x64) | ||||||
| Execution time | 153.16ms (± 0.20%) | 152.79ms (± 0.18%) | -0.37ms (- 0.24%) | 151.87ms | 157.17ms | p=0.000 n=600 |
| tsserver-startup - node (v18.15.0, x64) | ||||||
| Execution time | 229.03ms (± 0.16%) | 228.91ms (± 0.15%) | -0.12ms (- 0.05%) | 227.67ms | 232.49ms | p=0.000 n=600 |
| tsserverlibrary-startup - node (v18.15.0, x64) | ||||||
| Execution time | 231.42ms (± 0.19%) | 231.30ms (± 0.18%) | -0.12ms (- 0.05%) | 229.33ms | 238.19ms | p=0.031 n=600 |
| typescript-startup - node (v18.15.0, x64) | ||||||
| Execution time | 230.78ms (± 0.19%) | 230.77ms (± 0.18%) | ~ | 229.27ms | 236.83ms | p=0.987 n=600 |
- node (v18.15.0, x64)
- tsc-startup - node (v18.15.0, x64)
- tsserver-startup - node (v18.15.0, x64)
- tsserverlibrary-startup - node (v18.15.0, x64)
- typescript-startup - node (v18.15.0, x64)
| Benchmark | Name | Iterations |
|---|---|---|
| Current | pr | 6 |
| Baseline | baseline | 6 |
Developer Information:
@DanielRosenwasser Here are the results of running the top-repos suite comparing main and refs/pull/57230/merge:
Everything looks good!
@DanielRosenwasser Yes, that is exactly the behavior I'd expect in a structural type system, with a result of Set<{meow(): void; woof(): void}>. The type could still give completions based on the base type which must be overlapped, but this kind of intersection is definitely sensible as illustrated by an alternate example like { poisonous: true } & { mammalian: true } for a set of platypuses and echidnas 😄
I'm often frustrated by TS overreaching based on a comparability check (e.g. https://github.com/microsoft/TypeScript/issues/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 types of
union/intersection - the parameter container types.
The feedback for them was:
-
Methods like
unionandintersectionshould be generic and return a corresponding TypeScript type.unionshould return aSet<T | U>, andintersectionshould return aSet<T & U>. -
Methods should not expect their arguments to be related to the underlying container type. This risks accidental invocations of
intersectionthat are guaranteed to produce empty collections, but it has the benefit of allowing more symmetric operations.In other words, both
stringSet.intersection(stringOrBooleanSet)andstringOrBooleanSet.intersection(stringSet)are valid invocations of each other.I'll note that I am personally the most opposed to this behavior, but I was the lone dissenter. 🙂
-
With respect to
SetLike, @rbuckton raised the same points ofReadonlySetbeing overkill. I suggestedReadonlySetLikemight make more sense. Nobody else had a strong opinion.
@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.
WFM, thanks for the feedback. I'll get that pushed up later today.
Any opinions on isSubsetOf and friends? If not I'll default to making them generic, following point 2, even though (because they return booleans) this doesn't let you constrain the output.
Any opinions on
isSubsetOfand friends? If not I'll default to making them generic
I think the right thing for those is to take a Set<unknown> since the type parameter will never be observed.
In other words, both
stringSet.intersection(stringOrBooleanSet)andstringOrBooleanSet.intersection(stringSet)are valid invocations of each other.
This is generally what people expect of e.g. Array.prototype.includes as well, and I largely concur, but to my knowledge there's no way to allow that without also allowing stringSet.intersection(numberSet) (unless we get lower-bounded or comparability constraints), correct?
@DanielRosenwasser Done. Specifically:
union<U>(other: ReadonlySetLike<U>): Set<T | U>; // things in result are in either
intersection<U>(other: ReadonlySetLike<U>): Set<T & U>; // things in result are in both
difference<U>(other: ReadonlySetLike<U>): Set<T>; // things in result are in receiver
symmetricDifference<U>(other: ReadonlySetLike<U>): Set<T | U>; // things in result are in either
and then ReadonlySetLike<unknown> as the parameter for the three predicates.
@fatcerberus It's possible to validate as a function parameter with an approach like the one I mentioned in my original comment which could be leveraged here, but I assume that falls into the category of "too complex for builtin types."
Hmm, I have mixed feelings about the name ReadonlySetLike. While I get it--it doesn't have any of the mutating methods--it'd be weird (and maybe a bit misleading, for people reading the code) if I actually wanted to make a mutable setlike and had to extend ReadonlySetLike to get there.
Also it doesn't seem completely analogous to ArrayLike since the methods are defined to return a full-fledged Set...
but to my knowledge there's no way to allow that without also allowing...
Correct. "This is why we need Comparable<T>!" got brought up multiple times in the discussion 🙂
If we are seriously thinking about Comparable<T>, why not take the more conservative option first and then introduce Comparable?
The conservative approach still fails basic desirable things like subtyping the output or a.intersect(b) === b.intersect(a)
Hmm, I have mixed feelings about the name
ReadonlySetLike. While I get it--it doesn't have any of the mutating methods--it'd be weird (and maybe a bit misleading, for people reading the code) if I actually wanted to make a mutable setlike and had to extendReadonlySetLiketo get there.
While I'm fine with both SetLike and ReadonlySetLike, there are other names we could consider as well. In C++, the requirements for a Container are quite similar (i.e., has a size, is iterable), so Container<T> is a possible option if there's a strong negative sentiment towards ReadonlySetLike.
Also it doesn't seem completely analogous to
ArrayLikesince the methods are defined to return a full-fledgedSet...
These methods allocate and return a new Set no matter what object you pass as the argument so long as it has the minimum necessary API for the methods to succeed. In fact, ArrayLike and SetLike are nearly identical in their capabilities, but are tailored to their specific protocols. Both are minimal, read-only representations of their respective full implementations. ArrayLike has a read-only length, the ability to read a single element by index, and the ability to read all elements by index and length. While SetLike has a read-only size, the ability to test whether a single value is in its domain via has(), and the ability to read all elements via keys().
Ah, I think I misinterpreted ReadonlySetLike as being the interface that provided the new methods, which I'm guessing now isn't actually the case - that's the part I was objecting to, since they always return a real Set. My objections may have been misplaced :)