Skip to content
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

Chainable API #415

Open
mfulton26 opened this issue Aug 22, 2024 · 5 comments
Open

Chainable API #415

mfulton26 opened this issue Aug 22, 2024 · 5 comments

Comments

@mfulton26
Copy link

I just discovered this library and it looks great. What do you think of supporting a chainable API too?

Pure functions are nice but when a developer needs to use multiple methods in a row it gets annoying to have to define intermediary variables or to nest function calls. JavaScript doesn't have a pipeline operator but prototypes can be safely augmented these days using symbols (rather than strings which can cause conflicts because they are not unique).

Example

import $ from 'es-toolkit/$';
import 'es-toolkit/$/array/chunk';

// Splits an array of numbers into sub-arrays of length 2
[1, 2, 3, 4, 5][$.chunk](2);
// Returns: [[1, 2], [3, 4], [5]]

// Splits an array of strings into sub-arrays of length 3
['a', 'b', 'c', 'd', 'e', 'f', 'g'][$.chunk](3);
// Returns: [['a', 'b', 'c'], ['d', 'e', 'f'], ['g']]

Implementation

Augmenting modules should be able to be programmatically generated from pure functions (e.g. using ts-morph):

// es-toolkit/$.ts
export interface Symbols {};
export default {} as Symbols;
// es-toolkit/$/.symbols/chunk.ts
import $ from 'es-toolkit/$.ts';

const key = 'chunk';
const value = Symbol(`es-toolkit/${key}`);

declare module 'es-toolkit/$.ts' {
  interface Symbols {
    [key]: typeof value;
  }
}

Object.defineProperty($, key, { value });
// es-toolkit/$/array/chunk.ts
import 'es-toolkit/.symbols/chunk';
import { chunk } from 'es-toolkit/array/chunk';

const key = $.chunk;
function value<T>(this: T[], size: number): T[] {
  return chunk(this, size);
}

declare global {
  interface Array<T> {
    [key](size: number): T[];
  }
}

Object.defineProperty(Array.prototype, key, { value });
@D-Sketon
Copy link
Contributor

Do modifications to the Array.prototype make the sideEffects become true?

@mfulton26
Copy link
Author

Do modifications to the Array.prototype make the sideEffects become true?

I'm not familiar with that flag. Is that a webpack specific flag? I think it would, but the side-effects modules could be published under a different package name while reusing code to avoid such.

@mfulton26
Copy link
Author

After looking at some docs it looks like this can be set to true for only some modules. e.g.

  "sideEffects": ["./src/$/**/*.ts"]

@Smrtnyk
Copy link

Smrtnyk commented Oct 8, 2024

mutating Array.prototype doesn't sound like the best idea to me

@mfulton26
Copy link
Author

mutating Array.prototype doesn't sound like the best idea to me

Understandably IMO. Historically this is considered unsafe but with the introduction of Symbol in JavaScript this can now be done safely without name collisions (as is possible with String-based keys but not with unique Symbol-based keys). This is commonly done today with polyfills (e.g. via zloirock/core-js: Standard Library). It is this same polyfill idea/process but applied to user-defined prototype methods using Symbol keys instead of TC39-define prototype methods using String keys.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants