8000 .filter() typing breaks on unions of arrays · Issue #5928 · lodash/lodash · GitHub
[go: up one dir, main page]

Skip to content

.filter() typing breaks on unions of arrays #5928

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

Open
Roanmh opened this issue Oct 1, 2024 · 1 comment
Open

.filter() typing breaks on unions of arrays #5928

Roanmh opened this issue Oct 1, 2024 · 1 comment

Comments

@Roanmh
Copy link
Roanmh commented Oct 1, 2024

The following code errors for lodash but not for the similar implementation using the built in .filter(). I encountered this, but my minimal repro is inspired by @ialexryan 's report of microsoft/TypeScript#44373. This is also true for find, every, some, etc

import _ from "lodash"

interface Fizz {
    id: number;
    fizz: string;
}

interface Buzz {
    id: number;
    buzz: string;
}

([] as Fizz[] | Buzz[]).filter(item => item.id < 5); // ok

_.filter([] as Fizz[] | Buzz[]).filter(item => item.id < 5);
//                                                  ^^ error
Property 'id' does not exist on type 'number | Fizz | Buzz | { <S extends Fizz>(predicate: (value: Fizz, index: number, array: Fizz[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz, index: number, array: Fizz[]) => unknown, thisArg?: any): Fizz[]; } | ... 60 more ... | ((searchElement: Buzz, fromIndex?: number | undefined) => boolean)'.
  Property 'id' does not exist on type 'number'.

or view on TS Playground

Note: Typescript would error as well until this was fixed by Typescript 5.2.2

@ruchivora
Copy link
ruchivora commented Oct 7, 2024

When you use the native Array.prototype.filter() function on a union type (like Fizz[] | Buzz[]), TypeScript is more lenient and tries to infer the type based on the structural typing principle. Structural typing means that TypeScript checks if a type has the necessary properties at runtime, rather than enforcing strict type rules upfront.

TypeScript assumes that both Fizz and Buzz might have an id property, and it allows you to proceed without throwing an error. This leniency can lead to runtime errors if one of the types (e.g., Buzz) doesn't actually have the id property.

Lodash’s TypeScript type definitions are stricter because they want to ensure you don’t encounter runtime issues by accidentally accessing properties that don't exist on certain types. So you can use something like:

interface Common {
id: number;
}

interface Fizz extends Common {
// Other properties for Fizz
}

interface Buzz extends Common {
// Other properties for Buzz
}

_.filter([] as Fizz[] | Buzz[]).filter(item => item.id < 5);

Now _filter can be sure that both Fizz and Buzz has a id property.

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

No branches or pull requests

2 participants
0