I'd say you might need to be more specific with your phrasing.
If you want replicate more class-like behavior (but without actual class/inheritance information):
- Some type of exported constructor function
- The function instantiates an instance of data internally that is possibly mutable (let vs const)
- That function returns an object/interface of functions that have access to that data by virtue of it being defined in a higher but accessible function scope
- All other private functions are simply not exported
Or you might mean singleton data:
- let or const data at the file/module scope level
- export functions to operate on the data
- Don't export functions you want to be private
The first is sort of reinventing a class instance, but if you are anti-inheritance for the most part (likely a good idea), then you can use a "kind" or "type" property on the returned constructed plain object if needed (TypeScript generics make this quite ergonomic to represent as well). So, you don't really need classes, but they have their uses and have familiar and comfortable syntax to programmers from other languages. You can use either and they are both accepted as idiomatic.
I usually still allow classes in codebases that I have some control over, but add a custom ESLint rule to ban extension unless with an ignore comment. This sort of encourages you to compose more like the functional/data approach, but allows you to still use instanceof since it's an actual class. It also means you're not creating a copy of all the method functions if you don't need to between instances.
There is a caveat that there is a class syntax:
method = () => {}
That auto-binds the method to this, creating that method copy for all. In this case you're encroaching on the plain object behaviour, again with a more familiar syntax.
So, I guess it's more messy than I thought reflecting on it. But you have some choices depending on what properties you'd like to encourage in your codebase.