Preview:ts Object.groupBy()
You can choose specific lines to embed by selecting them before copying the link.
1 messages · Page 1 of 1 (latest)
Preview:ts Object.groupBy()
!ts
Object.groupBy()
// ^^^^^^^
// Property 'groupBy' does not exist on type 'ObjectConstructor'.
It seems like this should exist: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/groupBy#browser_compatibility
And it seems like this was merged in January: https://github.com/microsoft/TypeScript/pull/56805
So how do I use it?
make sure you have target set to esnext, as Object.groupBy is set for es2024
"typescript": "^5.6.3",
dang, I knew it was a tsconfig issue
tsconfig fixes are difficult to self serve
yeah it kinda is a matter of experience
Yeah, and I know this is not the right place to discuss it, but I think it indicates a smell with the language
Why is it the target? I thought that's about which polyfills is uses. Am I wrong?
ts only has polyfills for syntax features, not for library features
Now that I know it's about the tsconfig, I would've expected it to be a change in lib
lib is for more granular configuration for the same thing, for when you have a low target but you have polyfills for newer features (but not all of them)
in the tsconfig docs for lib:
TypeScript also includes APIs for newer JS features matching the target you specify; for example the definition for
Mapis available if target isES6or newer.
[...]
You may want to change these for a few reasons:
- Your program doesn’t run in a browser, so you don’t want the "dom" type definitions
- Your runtime platform provides certain JavaScript API objects (maybe through polyfills), but doesn’t yet support the full syntax of a given ECMAScript version
- You have polyfills or native implementations for some, but not all, of a higher level ECMAScript version
Hrm. Right now I'm using "target": "es2018",. Changing that to esnext will bring lots of changes with it, changes that might break backwards compatibility for users of older browsers, right?
glad I asked
that's weird because the compatibility graph seems to imply it's available to most
nm
I misread the top of the MDN article. I thought it said this was new in 2022, but it's new in 2024
if you want to provide a polyfill for this specifically, you would add es2024.object (or maybe esnext.object, depending on ts version) to tell ts about it
seems like esnext.object for the current release (5.6), and would be es2024.object in the next major release (5.7)
you would add es2024.object (or maybe esnext.object, depending on ts version) to tell ts about it
Add it as a lib?
yep, sorry
Sorry if you already explained this but why do I get this warning?
Side note: I think I'm reading that you have to run npm install after you change lib. I didn't realize there was anything like that
do you get the warning on the command line? im thinking it might just be an outdated schema
that file definitely exists in 5.6.3
no but I do get these errors:
you shouldn't have to, no.
lib doesn't affect modules, it's just an option for tsc
node_modules/lru-cache/dist/commonjs/index.d.ts:1032:5 - error TS2416: Property 'forEach' in type 'LRUCache<K, V, FC>' is not assignable to the same property in base type 'Map<K, V>'.
Type '(fn: (v: V, k: K, self: LRUCache<K, V, FC>) => any, thisp?: any) => void' is not assignable to type '(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any) => void'.
Types of parameters 'fn' and 'callbackfn' are incompatible.
Types of parameters 'map' and 'self' are incompatible.
Type 'LRUCache<K, V, FC>' is not assignable to type 'Map<K, V>'.
The types returned by 'entries().next(...)' are incompatible between these types.
Type 'IteratorResult<[K, V], void>' is not assignable to type 'IteratorResult<[K, V], undefined>'.
Type 'IteratorReturnResult<void>' is not assignable to type 'IteratorResult<[K, V], undefined>'.
Type 'IteratorReturnResult<void>' is not assignable to type 'IteratorReturnResult<undefined>'.
Type 'void' is not assignable to type 'undefined'.
1032 forEach(fn: (v: V, k: K, self: LRUCache<K, V, FC>) => any, thisp?: any): void;```
that's an error internal to one of your deps, you can ignore that for now
I'm pretty sure I wasn't getting those before I modified my lib, but undoing of the change doesn't make them go away
you could enable skipLibCheck to handle that (given that the library should already be checked before publishing)
i don't remember if that's recommended exactly though
They make tsc error
there may be caveats that i'm forgetting
(btw for node projects you should use module: node16/nodenext)
so it's not giving an error on the command line?
Nope
i guess codelens has an outdated list then
This is a node project that transpiles to the browser. Does that matter?
probably with the schema it has
where does it run? is it like vite or something like that
i have no idea what you mean by "transpiles to the browser"
This TS transpiles to a frontend app
it doesn't run in the backend only in the browser
I'm not sure if that made more sense
it didn't tbh
what do you mean by "node project", then?
run in the backend only in the browser
these are different distinctions
frontend vs backend is a distinction of function
server vs client (browser) is a distinction of location
frontend apps still need a server to send the html and js and everything
it runs on the client browser
is there any part that runs on node?
I know this sounds weird, but there is no server
ie, the server
no
how are you serving the frontend then
unless you're just opening html files, you kinda need a server
okay this is kind of convoluted but there is another tool I run after tsc that builds the JS into an HTML file
(i guess the filesystem is the server in that case..)
Well technically I do have a server but it serves static content
is it on node
the server? I don't know. It's whatever my domain/host provider uses. I just ftp it to a location and it is accessible to the world
what did you mean by "node project" here then, im so confused lmao
Well it was a reply to this
Maybe I don't understand what the module attribute means, but I thought I did
that doesn't answer my question at all lol
why'd you say it was a node project if no part of it involves node
it probably does mean what you think it means; there's just some nuance with node
module: es6 means esm, module: node16 means, "idk, ask node"
so it's aware of package.json, of cjs/mjs file extensions
so there's a single source of truth about module system
so it's aware of package.json, of cjs/mjs file extensions
What's "it" here?
okay I hope this doesn't add more confusion but here's something: the JavaScript I generate must be a script
as in, I cannot use modules in the generated code
why not?
I'm using a tool that can't parse them when it generates the HTML file.
...what....
I didn't write this tool
yeah it's got over a decade of backwards compatibility concerns. It was written before modules existed
why are you using it
if it's bundling js into an html, it wouldn't need to parse anything
Mind if I DM you the reason?
no dm's, please.
your entire workflow seems like it'd be trivialized by a single bundler tbh
Well, I am using rollup as a later step
iirc some do polyfills as well so that'd handle that for you
what would you bundle after everything's a single html wtf 😭
how long is this toolchain
you don't need a tool for that
I can't change that
Right, I understand, but the tool does lots of other things that benefit me
care to elaborate?
I'd be happy to but I prefer to do it in a DM
if you don't think they should be public then you shouldn't be telling them to a stranger
we don't need to take the conversation there, just to answer that specific question
but aight
aight, dm?
the ts -> js -> bundle could be a single step though
no lol
no, that's pretty much deprecated
most bundlers understand ts and can go straight from ts to a bundle
oh. What then?
TBH if you've got a weird setup that you can't really touch, maybe just pull in a groupBy utility, like the lodash one.
here's how you teach rollup to understand ts https://www.npmjs.com/package/@rollup/plugin-typescript
Yep that's what I was going to do next
I think I've already successfully done this in another project
Or maybe it was in this one, but some super obscure issue came up that I either couldn't figure out or couldn't be solved due to limitations of the tool
typically if you have a bundler, you also let the bundler handle transpilation, and tsc is treated as a checker
Yeah, I think I learned that after I setup this project
it's not a lot of setup on the tsc side (noEmit: true, scripts > build: "tsc && <bundler command>")
i can't comment on the bundler side though
right
I don't mind continuing to talk about this, but when I tried to run the code, I got this runtime error: TypeError: Object.groupBy is not a function
Even though it now compiles
And I'm perfectly content to use lodash instead. It's already a dependency but I was thinking about removing it long-term. Maybe I'll just keep it in if I have to use its groupby
TS doesn't insert a definition for libs, so wherever the code is running must not have groupBy yet.
did you add a polyfill like i mentioned?
is it being loaded before everything else?
maybe I misunderstood. I thought you were saying I only need to add to the lib
okay after reading the lib ref doc a 3rd time, I see where I misunderstood
maybe i was unclear, the lib addition would only tell ts, it wouldn't automatically add the polyfill
yeah I thought it added a polyfill too. my mistake
let me see. I don't know if I've ever intentionally used a polyfill before
okay I immediately got in over my head. I thought that all I would need to do was install this: https://github.com/es-shims/Object.groupBy
But when I look at the examples they have this line: var groupBy = require('object.groupby');
I know how to translate that into ESM, but isn't the point of changing lib to make it so I can write my code without that line?
Is there an official polyfill directory meant to be used with TypeScript's lib?
I know how to translate that into ESM, but isn't the point of changing lib to make it so I can write my code without that line?
no,libinforms ts what's available. it tells ts what you should be able to do. it doesn't affect what you can actually do
to make Object.groupBy actually work, you would do
import groupBy from 'object.groupby';
Object.groupBy = groupBy;
ahhh!
wait hold on i forget how to use this
okay 🙂
This feels kind of ... Why wouldn't I just depend on the polyfill and revert the lib?
I'm assuming that the polyfill has types I could depend on any other package's types
it does, for groupBy, not for Object
it does what?
oh wait no it doesn't, just via dt
Polyfilling is useful if you've got existing code that expects certain APIs to exist... but yeah, I'd just import a groupBy function and use it, personally.
@digital flame Isn't this the opposite of code that expects certain APIs to exist?
right, it's not the existing code
it's saying, they don't exist unless you manually import them and manually assign them to the Object
You polyfill in order to be able to run some code that requires the polyfilled stuff.
Like maybe I'm using a library and the author of the library assumes that Object.groupBy exists.
(That'd be kind of annoying given how new it is, but it's a more reasonable choice for older APIs)
object.groupBy doesn't actually do a polyfill. it provides an implementation of groupBy, so it has types for that function (via dt), but it doesn't declare that function on Object, because that function doesn't necessarily exist on Object
If I want to use that library, either I have to monkey around with their code to fix it or, more realistically, I polyfill Object.groupBy so the library's code works
But the page says, "An ESnext spec-compliant Object.groupBy shim/polyfill/replacement that works as far down as ES3." Is that just wrong?
No?
this must be super frustrating for you guys, hah. I think there's something I don't know I don't know which is causing this back-and-forth
it's used for a polyfill, but actually attaching the polyfill is something you have to do
"using a polyfill" would be attaching it to the object it's supposed to be on
It's just a bit of ambiguous terminology.
okay I think I get it then
People say "polyfill" to mean "function that implements a standard API" even if it's not automatically attached.
I remember there was an attempt to get "ponyfill" to catch on as the term for "implementation for a standard API that you use directly, rather than monkey-patching the runtime" but it really didn't.
as opposed to say, corejs which binds the polyfill for you, afaict
I think corejs provides both versions
It's my first time using a polyfill so, I assumed the dependency would have a side-effect that runs this code ^
Sometimes they do.
Looks like core-js has a version that automatically patches the runtime and core-js-pure which is the ""ponyfill"" version
okay back to TypeScript land: why should I bother modifying lib when I could just npm install object.groupby; npm install -D @types/object.groupby; and use the library like this: import groupBy from 'object.groupby';\n const x = groupBy(...)
depends if you want the function or the polyfill
Is it because this only came up by me choosing a "ponyfill"?
which, as retsam said, you should probably just go with the free function rather than messing with polyfilling it
If I chose a different library that automatically attached the polyfill, I guess I would use lib
Yeah. lib tells TS what features are supported in the runtime, either natively or via polyfills.
For just one function I think avoiding it is easier; polyfills can be useful in bulk - if I'm trying to make code run on IE for some reason, I can just use something like core-js and say "polyfill everything up to ES2023" and it works.
Yep that makes sense to me
Couldn't/doesn't core-js have types I could use to get that same functionality without touching lib?
maybe I should say that differently: couldn't I use the core-js types as a dependency and still avoid using lib?
If yes, I don't get when lib is preferred
I don't think most libraries that polyfill the runtime code modify the types automatically.
By that do you mean, "most polyfills don't have ts types?"
getting hung up on the word "automatically"
I mean the types like Object.groupBy.
If you do
import "core-js/polyfill-everything" // not the real import
that "automatically" attaches groupBy to Object but probably doesn't modify the TS types so that TS knows that Object.groupBy is a thing.
Okay. Think what you're saying is I would have to write the declaration for Object.groupBy after depending on the types. The types would only give me groupBy
😅 Not really.
gah
import "core-js/polyfill-everything" // not the real import
Object.groupBy()
// ^^^^^^^
// Property 'groupBy' does not exist on type 'ObjectConstructor'.
I think that's what I'm saying?
I'm not sure what you mean by "the types".
groupBy() exists as a type, but not on Object
It doesn't exist as a standalone function at all.
Not with a real "modifies-the-global-environment" polyfill library.
But shouldn't that kind of polyfill provide types that modify the Object declaration? I think what you said here is the answer to that question but I don't understand what it means when you say: "...TS knows that Object.groupBy is a thing."
I'm not sure what it means for "TS" to know
Pretty sure they don't.
okay I think I might be getting it
if the polyfill types don't do that, lib is the only way to inform ts
if the polyfill types did do that, I could add the types to package.json instead of using lib?
Polyfill types kind of can't do that because imports can't really have type-level side-effects
I don't think there's a way to write types that apply if I do import "core-js/polyfill-everything" and aren't applied if I don't.
Really? Maybe that's why am so confused. For example, I thought simply by having a declaration for a global property somewhere in your project, ts knows about it everywhere, even if I don't import it
Yes, "even if you don't import it".
If "core-js" had a definition for
// Modifies the global Object type to have this property
interface ObjectConstructor {
groupBy: /* definition of groupBy */
}
that defintion would be applied even if you didn't import the polyfill. (Which doesn't work when your package has lots of polyfills and the user picks which ones they import)
okay and I think you're saying that that would be bad because not everybody would want that universally
Yeah. (See everyone accidentally ending up with @types/node globals because it's installed by some dependency-of-a-dependency)
ahh okay
until that comments, I don't think I understood the big picture ramifications
I think we are talking past each other a little bit because you are using something large in scope like core-js whereas I'm thinking small scope like object.groupBy
if I didn't want to polyfill and automatically modify Object , I would just avoid installing that library
Well and make sure no dependency has it listed as a dependency.
isn't it a bad idea to use a type package as a regular dependency anyway?
It wouldn't be a "type package" if it's a polyfill.
(Also it's correct to put @types in regular dependencies if your API depends on the types in that package)
oh I never thought of that. That makes sense
It's kinda bad practice for libs to depend on polyfills anyways so the dependency-of-a-dependency probably would be some sort of dev/normal dependencies mixup, but weirder mistakes have happened.
Still getting this after reverting everything. Is this a reasonable hypothesis: I ran an npm i and it upgraded some sort of minor lib that caused this problem, something I wouldn't have caught because my package-lock.json isn't committed (shame on me. I thought it's been committed this whole time)?
Possibly, yeah.
@types packages are particularly squirrely because patch changes can be breaking.
I also feel like it's a huge issue that they don't correlate exactly with the runtime code version numbers. I don't know how to fix it, but it's a problem IMHO
They do correlate
It's kinda bad practice for libs to depend on polyfills
lmao...
@types/package: "x.y.z" corresponds to package version "x.y".
if they're in the same repo yeah. You mean even if they're separate?
If they're separate, yeah.
maybe that's ideal? I think I've found libs that don't line up like that
It's pretty well followed. Though if a version doesn't have type changes there may not be a @types version so you just go back.
or maybe I just assumed they don't correlate because I saw the different patch number
e.g. if I release [email protected] and there's no type changes from [email protected] there's probably no reason why there'd be a @types/[email protected]
So I'd use [email protected] and keep using @types/[email protected] and that's fine.
okay that would definitely have led me to believe they don't correlate.
Makes sense. But they do correlate. That's why patch versions are semver-breakingly dangerous for @types.
I modified my package.json to remove all the ^ from semvers, I removed the node modules directory, and then I installed. I can now compile. Does that prove that my hypothesis was correct?
I guess there's an extremely unlikely chance that somebody caught their mistake and fixed it without bumping a version number
Other than that...
There's also just some chance that there's spooky things going on in a cache on your machine somewhere.
But yeah, probably.
I've learned a lot from this conversation but I think I've taken it off topic too. Thank you both for educating me tonight. I appreciate it and I think I got the answer I'm looking for!