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

A couple of suggestions #509

Open
lensbart opened this issue Feb 12, 2024 · 2 comments · May be fixed by #525
Open

A couple of suggestions #509

lensbart opened this issue Feb 12, 2024 · 2 comments · May be fixed by #525

Comments

@lensbart
Copy link
Contributor

lensbart commented Feb 12, 2024

Hi,

Thanks for this library, it works well. I recently moved away from using Ramda because I didn’t like that the keys used by omit weren’t narrowly typed.

As part of that migration, I had to move a couple of functions that are not implemented by Remeda into my own repository. I think these could be well-suited for inclusion:

/**
 * similar to https://ramdajs.com/docs/#has, but uncurried and with the
 * opposite order of arguments
 */
export const has = <T extends number | string | symbol>(
  obj: unknown,
  key: T,
): obj is Record<T, unknown> =>
  typeof obj === 'object' && obj != null && key in obj

/** see https://ramdajs.com/docs/#sum */
export const sum = (list: number[]) => list.reduce((a, b) => a + b, 0)

/** see https://ramdajs.com/docs/#toLower */
const toLower = <T extends string>(string: T) =>
  string.toLowerCase() as Lowercase<T>

In addition, I find the following utility function useful, because their return value is typed as a string literal, if called with a string literal argument:

export const capitalize = <T extends string>(key: T) =>
  `${key[0]?.toUpperCase() ?? ''}${key.slice(1)}` as Capitalize<T>

export const uncapitalize = <T extends string>(key: T) =>
  `${key[0]?.toLowerCase() ?? ''}${key.slice(1)}` as Uncapitalize<T>

Finally, unreachable can be a handy function to enforce exhaustiveness in a switch/case, either when you don’t use typescript-eslint to do this check for you, or if you want to have absolute certainty that there will be no fallthrough (e.g. in case of a stale frontend receiving data from a freshly updated backend). You can use this function in the default case, to ensure via the type system that you handled all cases in the cases above:

/** Workaround for https://github.com/typescript-eslint/typescript-eslint/issues/2841 */
export const unreachable = (never: never) => {
  throw new Error('Unreachable', { cause: never })
}
@eranhirsch
Copy link
Collaborator

eranhirsch commented Feb 13, 2024

  • has was suggested in [Feature Request]: Object property predicates like hasProp (includes proof of concept) #461 as hasProp and would be added soon as part of closing that issue.
  • sum can be implemented easily: R.sumBy(R.identity), there's not much value in including it as a standalone function.
  • toLowerCase, toUpperCase, capitalize, and uncapitalize are all great additions, especially as a dataLast function that could be used in pipes. Send a PR, and I'll approve them.
  • unreachable - I'm not sure... it feels like a code smell if you need to use it considering there are non-runtime ways to avoid it via typescript itself and eslint. Adding a utility like this can mislead people into opting for sub-optimal solutions, which I prefer we avoid.

@lensbart
Copy link
Contributor Author

@eranhirsch I opened a PR with toLowerCase, toUpperCase, capitalize and uncapitalize, as suggested. Note that both Ramda and Lodash use toLower/toUpper nomenclature.

Regarding

sum can be implemented easily: R.sumBy(R.identity), there's not much value in including it as a standalone function.

I think that R.sumBy(R.identity) is unnecessarily noisy to sum an array of numbers. So in my view, it can have its place in a utility library. But of course, it’s easy to implement in user land.

it feels like a code smell if you need to use it considering there are non-runtime ways to avoid it via typescript itself and eslint

TypeScript and ESLint don’t offer a complete guarantee here, as mentioned above, in the case of a long-running (stale) frontend communicating with a recently-updated backend. In addition, there are cases not yet understood by ESLint such as mentioned here, where unreachable would help satisfy the linter via the default case.

Just adding this FYI, I won’t go and open a PR considering that it’s not wanted.

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