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

feat: add findFirstType method to Component #6084

Merged
merged 3 commits into from
Aug 25, 2024

Conversation

WilliamDASILVA
Copy link
Contributor

Problem

Currently, the findType method in the Component class returns an array of components (Component[]). This can lead to potential type-safety issues when developers need to access the first element of the result. For example:

const tileContent = this.findType('tile-body')[0];
tileContent.someMethod(); // Potential runtime error if tileContent is undefined

We've explored several approaches to address this issue:

  1. Using optional chaining:

    const tileContent = this.findType('tile-body')[0];
    tileContent?.someMethod();

    While this prevents runtime errors, it doesn't fully leverage TypeScript's type system to catch potential issues at compile-time.

  2. Using type assertions:

    const tileContent = this.findType('tile-body')[0] as Component;

    This approach suppresses TypeScript warnings but doesn't actually solve the underlying issue and can lead to runtime errors.

  3. Using Array.prototype.at():

    const tileContent = this.findType('tile-body').at(0);
    if (tileContent) tileContent.someMethod();

    This approach is better but still requires explicitly include the .at(0) call, which can be error-prone.

We've also attempted to modify type definitions to make findType return (Component | undefined)[], but this led to confusing type information and didn't solve the core issue, or wrap it with lodash's first method but
These workarounds, while functional, don't provide an ideal solution that combines type safety, clarity, and ease of use. We believe a more robust solution is needed to address this issue consistently across our codebase.

Proposed solution

To address this, we propose adding a new method findFirstType to the Component class. This method will return either a Component instance or undefined, making it clearer when a component of the specified type is not found.

The existing closestType method already follows this pattern, so adding findFirstType will not only improve consistency but also provide a more type-safe and intuitive way to access the first component of a given type.

@artf
Copy link
Member

artf commented Aug 23, 2024

I was actually thinking of making findType a generic one, so it could allow something like this.findType<ComponentText>(...).

What was the issue with (Component | undefined)[]? For the generic case, I was expecting to return (T | undefined)[]

@WilliamDASILVA
Copy link
Contributor Author

@artf

This type suggests that any element in the array could be either a Component or undefined, which is not accurate. In reality, findType always returns an array of Component instances or an empty array. It never includes undefined elements.

This creates a subsequent issue: it will require null checks on every element of the array, even in scenarios where we know it's impossible. Typically, in a loop:

const tileBodies = this.findType('tile-body') as (Component | undefined)[];

for (const component of tileBodies) {
   // We know "component" cannot be undefined here because we're looping through existing components; however, we still need to check for it if we added hypothetical undefined elements to the array.
   if (component) component.addAttribute()

   // or
   component!.addAttribute()
}

I though at first if we could gets closer from the JS Array's find vs. filter methods philosophically, or even Backbone's findWhere but that would probably a too big breaking change.

Generic types in addition for those methods could be pretty useful too and would love to see it.

@artf
Copy link
Member

artf commented Aug 25, 2024

Thanks for the explanation @WilliamDASILVA I didn't consider the issue with iterators 🤦‍♂️

From what I've seen it is a common issue handling empty arrays in TS.

Anyway, I think findFirstType is a really small but nice addition to the Component APIs as I found myself using findType('...')[0] quite a lot 🙇‍♂️

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.

2 participants