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

(fix): infer return type in "or" and "and" helpers #185

Closed
wants to merge 2 commits into from

Conversation

SergeAstapov
Copy link
Collaborator

@SergeAstapov SergeAstapov commented Aug 9, 2023

with v4.0.0-beta.0, return type for and and or is boolean which does not match the semantics of the helper itself.

see https://github.com/jmurphyau/ember-truth-helpers/blob/v4.0.0-beta.0/packages/ember-truth-helpers/src/helpers/or.ts#L9

This PR tries to infer the return type from the arguments, kinda similar to what was previously done in https://github.com/Gavant/glint-template-types/blob/v0.3.5/types/ember-truth-helpers/or.d.ts without limiting to 5 arguments.

This still does not work as intended, so more work needed.

@SergeAstapov SergeAstapov changed the title (fix): infer return type in or helper (fix): infer return type in "or" and "and" helpers Aug 9, 2023
Copy link

@chriskrycho chriskrycho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably want something like this:

import { assert } from '@ember/debug';

/**
 * Return first truthy value, otherwise the last value
 *
 * @param positional - array of values
 */
export default function or<T extends Array<unknown>>(
  ...positional: T
): T[number] {
  assert(
    `must pass 2 or more arguments to 'or' helper, passed ${positional.length}`,
    positional.length >= 2
  );

  // Falling back to the final value makes this helper behave the same way as
  // the JavaScript `||` operator.
  return positional.find(Boolean) || positional[positional.length - 1];
}

function and<T extends unknown[]>(...params: T): T[number] {
  assert('`and` helper expects multiple parameters', params.length > 1);
  return params.reduce((a, b) => a && b, true);
}

You probably want something like this:

import { assert } from '@ember/debug';

/**
 * Return first truthy value, otherwise the last value
 *
 * @param positional - array of values
 */
function or<T extends Array<unknown>>(
  ...positional: T
): T[number] {
  assert(
    `must pass 2 or more arguments to 'or' helper, passed ${positional.length}`,
    positional.length >= 2
  );

  // Falling back to the final value makes this helper behave the same way as
  // the JavaScript `||` operator.
  return positional.find(Boolean) || positional[positional.length - 1];
}

function and<T extends unknown[]>(...params: T): T[number] {
  assert('`and` helper expects multiple parameters', params.length > 1);
  return params.reduce((a, b) => a && b, true);
}

Given you're shipping a breaking 4.0 release, and that there is a polyfill with support for functions-as-helpers which supports 3.28 LTS, I think you can and should just use these. If, however, you decide there is a reason to stick with helper, you will need to change it slightly, but do not have to ship a standalone Signature (we went out of our way to make sure inference works correctly with an "inline" definition):

import { helper } from '@ember/component/helper';

// or.ts
const or = helper(function or<T extends Array<unknown>>(
  positional: T
): T[number] {
  // same implementation ...
});

// and.ts
export default helper(function and<T extends unknown[]>(
  positional: T
): T[number] {
  // same implementation ...
});

@SergeAstapov
Copy link
Collaborator Author

we went slightly different route with #188

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

Successfully merging this pull request may close these issues.

Incorrect glint return type for or helper in 4.0.0-beta.0
2 participants