Skip to content

fix(theme-language-server-common): preserve element type on array-returning filters#1184

Open
Jp-fix wants to merge 1 commit into
Shopify:mainfrom
Jp-fix:fix/1086-preserve-element-type-on-array-filters
Open

fix(theme-language-server-common): preserve element type on array-returning filters#1184
Jp-fix wants to merge 1 commit into
Shopify:mainfrom
Jp-fix:fix/1086-preserve-element-type-on-array-filters

Conversation

@Jp-fix
Copy link
Copy Markdown

@Jp-fix Jp-fix commented Apr 18, 2026

What are you adding in this PR?

Fixes #1086.

When a typed value is piped through an array-returning filter like sort, the language server was showing untyped[] on hover instead of preserving the element type. Example from the issue:

{% for media in product.media %}
  {% assign media_as_array = media | sort %}
  {# hover on `media_as_array` used to show `untyped[]` — now shows `media[]` #}
{% endfor %}

The filter docs declare array_value: "untyped" for these filters, so docsetEntryReturnType was always handing back untyped[]. Instead of changing the docs (they're pulled from Shopify/theme-liquid-docs), I added a small pass-through list in TypeSystem.ts covering sort, sort_natural, reverse, uniq, compact and where, and switched the filter chain from "only inspect the last filter" to a left-to-right reduce so chains like x | sort | reverse thread the element type correctly.

Scalar inputs (media | sort in the issue) are wrapped into an array of the same type, matching Liquid's runtime coercion.

What's next? Any followup issues?

  • `map` is out of scope here — it would need to resolve a property lookup on the element type. Worth a separate PR.
  • `concat` is also out of scope: the result type would be a union of both arrays' element types, which the type system doesn't represent today.

Before you deploy

  • I included a patch bump `changeset`

@Jp-fix Jp-fix requested a review from a team as a code owner April 18, 2026 20:15
@Jp-fix
Copy link
Copy Markdown
Author

Jp-fix commented Apr 18, 2026

I have signed the CLA.

Copy link
Copy Markdown
Contributor

@aswamy aswamy left a comment

Choose a reason for hiding this comment

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

Thanks for the contributions Jp!

I've left a few suggestions in the PR. Also, please follow the PR template for your PR descriptions

https://github.com/Shopify/theme-tools/blob/main/.github/PULL_REQUEST_TEMPLATE.md

'@shopify/theme-language-server-common': patch
---

Preserve the element type when piping a typed value through `sort`, `sort_natural`, `reverse`, `uniq`, `compact` or `where`. Previously these filters collapsed the type to `untyped[]` because their `array_value` is declared as `untyped` in the filter docs; they are now treated as pass-through in the type system. Fixes #1086.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Keep the changeset short and sweet. No implementation details or ticket numbers.

Comment on lines +607 to +624
/**
* Filters whose return type is an array of the same element type as their
* input. The filter docs in `filters.json` declare `array_value: "untyped"`
* for these, which would make every pipeline collapse to `untyped[]`. We
* special-case them here to preserve the element type of the input.
*
* Intentionally excluded:
* - `map` — projects a property, needs lookup resolution on the element type.
* - `concat` — merges two arrays, element type would be a union.
*/
const ARRAY_PASSTHROUGH_FILTERS: ReadonlySet<string> = new Set([
'sort',
'sort_natural',
'reverse',
'uniq',
'compact',
'where',
]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Instead of having a static allow-list, could we invert this and exclude all array filters that isnt map or concat.

'where',
]);

function elementTypeOf(t: PseudoType | ArrayType): PseudoType {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Have a comment that mentions that non-array inputs are treated as a single-element array of that type

Suggested change
function elementTypeOf(t: PseudoType | ArrayType): PseudoType {
/* Returns the element type of an array, or the input itself if it is a scalar. */
function elementTypeOf(t: PseudoType | ArrayType): PseudoType {

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

Successfully merging this pull request may close these issues.

Improve doc typing when converting to array

2 participants