You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Passing valid predicate types to Array.filter is an error in version 5.2.2.
The following code produces errors in both version 5.1.6 and 5.2.2,
but the errors are are different and specify different locations.
In 5.1.6 the error is on filter, in 5.2.2 it is on myPredicates.
declareconstarrsn: string[]|number[];declarefunctionmyPredicate(value: number,index: number,array: number[]): boolean;declarefunctionmyPredicate(value: string,index: number,array: string[]): boolean;constr1=arrsn.filter(myPredicate);// 5.1.6 error: (on filter)// This expression is not callable.// Each member of the union type // '{ <S extends string>(predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): S[]; // (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): string[]; } | { ...; }' // has signatures, but none of those signatures are compatible with each other.(2349)// 5.2.2 error: (on myPredicate)// No overload matches this call.// Overload 1 of 2, '(predicate: (value: string | number, index: number, array: (string | number)[]) => value is string | number, thisArg?: any): (string | number)[]', gave the following error.// Argument of type '{ (value: number, index: number, array: number[]): boolean; (value: string, index: number, array: string[]): boolean; }' is not assignable to parameter of type '(value: string | number, index: number, array: (string | number)[]) => value is string | number'.// Types of parameters 'value' and 'value' are incompatible.// Type 'string | number' is not assignable to type 'number'.// Type 'string' is not assignable to type 'number'.// Overload 2 of 2, '(predicate: (value: string | number, index: number, array: (string | number)[]) => unknown, thisArg?: any): (string | number)[]', gave the following error.// Argument of type '{ (value: number, index: number, array: number[]): boolean; (value: string, index: number, array: string[]): boolean; }' is not assignable to parameter of type '(value: string | number, index: number, array: (string | number)[]) => unknown'.// Types of parameters 'value' and 'value' are incompatible.// Type 'string | number' is not assignable to type 'number'.// Type 'string' is not assignable to type 'number'.(2769)
π Actual behavior
Error, as shown above.
Also, the return type (string|number)[] is not correct - it should be string[]|number[]. I understand that the overly widened return type was thought to be better than nothing, However, since the new behavior in 5.2.2. is still buggy, perhaps the final fix could could also return the correct type.
π Expected behavior
Emit no error.
Additional information about the issue
Note that using a template function does work with current 5.2.2 version:
declare function myPredicateTpl<T>(value: T, index: number, array: T[]): boolean;
const r2 = arrsn.filter(myPredicateTpl); // no error in 5.2.2., return type is (string|number)[]
However, the behavior described by passing the overloaded myPredicate template function as not the same is not the same as the behavior described by passing myPredicateTpl . Only the former describes the scenario such that the same type is used for all index values and that the array has single valued element type.
We can actually generate a working version of this using the following distributed template function:
type ArrayFilterFunctionType<ArrayType extends readonly unknown[]> =
ArrayType extends (infer ET)[] ?
(predicate:(value: ET, index: number, array: ArrayType) => unknown, thisArg?: any) => ET[]
: never ;
declare interface ArrayWithWorkaroundForFilter<AT extends unknown[]> {
filter: ArrayFilterFunctionType<AT>;
}
const r3 = (arrsn as ArrayWithWorkaroundForFilter<typeof arrsn>).filter(myPredicate);
// accepts myPredicate and also has correct return type "string[] | number[]"
It also actually returns the correct type string[] | number[].
Here we can see it describes a union of signature types:
// For human perusal
type T = ArrayFilterFunctionType<typeof arrsn>;
// Tooltip shows
// type T =
// ((predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any) => string[])
// | ((predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => number[])
While currently TypeScript has the unfortunate limitation of only being able to return string[] | number[],
it may at some point be improved to overcome that limitation and return the accurate type. For that reason
it is better to encourage habitual use of a predicate description which actually describes the behavior of the predicate.
I guess putting a distributed template type like ArrayFilterFunctionType in a TypeScript lib file would be probably be too computationally expensive. However, if the same thing could be achieved with oneof -like notation and implementation, then it could become cheap enough.
Finally, note that adding a catch all function with return type never to the overaloads will get rid of the error:
declare function myPredicate(value: number, index: number, array: number[]): boolean;
declare function myPredicate(value: string, index: number, array: string[]): boolean;
declare function myPredicate(value: any, index: number, array: any[]): never;
const r1 = arrsn.filter(myPredicate); // no error in 5.2.2, return type (string|number)[]
However, it returns (string|number)[], not the correct return type string[] | number[].
The text was updated successfully, but these errors were encountered:
The predicate doesnβt look to be valid for the type of the array?
If the array is (A[] | B[]), in the context of the array.filter predicate, each item is A | B, so the predicate that forces (item: A) breaks that expectation and is not compatible.
The way I see it, that value predicate should fail. There is no guarantee from a type system perspective that item is an A, so a predicate of (item: A...) isnβt compatible.
It is predicates in the plural. So it will be either element A with array A[], or element B with array B[]. No mixing.
That's what the overload means.
It's up to the user implementation to implement that correctly. Are we on the same wavelength now?
In isolation, each of the myPredicate types is invalid, but with the overloading, it should be valid (but it is not valid with the current implementation of "unioning" the array types to merge the definition). I see what youβre saying here.
Really the type of (A[] | B[]).filter should boil down to something like this for any members of the union.
filter(predicate: ((value: A, index: number, array: A[]) => value is A) | ((value: B, index: number, array: B[]) => value is B), thisArg?: any): A[] | B[]
π Search Terms
Add fallback logic for generating signatures for unions of array members #53489
π Version & Regression Information
β― Playground Link
https://www.typescriptlang.org/play?ts=5.2.2#code/CYUwxgNghgTiAEYD2A7AzgF3rGaUC55MYBLFAcwG0BdeAH3hQFcBbAIxBhoG4AoX0JFgIAZkxRgMJVPBYBPAApxgJMFAwgAFADcoEJiELN2nADTwyoAB5HWHGOZxQ5tk12oBKQmyRIIIKBQ+QWg4eDEJKRl5JRAVNQ0dPQNCYjJyc0sQG0Y7M2wYGGdUjFIKGi94Hz8AoP5kdCwYAEZ4AF4C3BQAOhESCA0YTRjlVXUQDz5eAHpp+ABWbubugDZ4ThgkGEJNGT6Bzg8ZuYAVAAsSNHWrAAc4NDRpFAsrlCQsNQhoNn9u4-h4ABRKBgM6yEBueBIETwDBnBDiJ6wuQ3BD-eAAcgA3vAADwAZWuGhQwCuaQoAD5NHc4mMNDtdPpDERSulMiTsq57I5CsUWWUqJ52hT4IyDC94PjzHDLgBBGDkAD8hECckq+J48HRAPg1NGCWZSSZJQF7OsXPyThc-PSFWF8HEAGs3gB3FDSi5oeVKlUoNUm23UbjwAC+9HgOO6UeDIYxWtmOrOUDJJHIKHUTHu5jYTCwbxQCGhsLOSDQCEeaYz9wKCGQLBu6hIPwQLpIcPWILB73hMG6mgATABmAAsAE4jv9Fv3uv31oUtjtoop9eMjgmAHJIKHaTgQJBQYCydSgkBXGVXT4QP4JgEAeR3MD3B-grSL-fMGL1tINDOSzPJ5DhsY3IWByOTAZavLWpoAFAXkMAeHabQimKCCXDaFBwW4HpygqyrYH6lQwaymEMBBCE0Bi5jkFAO7FqIfh7q2mEbFs15zDq8DeqwIAoFgRYYCiCDYrqqEWg4oHmrk2GdHy5EVN4vj+IEwZGikGEZJJnLSSBVoBuUniKTUKmhnG6FvFgyYVumzawluDZFCwICDFCMKCaimJqf+JGAWR8Fmtp5E8kU0GwX5biIUKyGin+EphTpnAYuxnE6icQlXEWDlQE5gxXBiqFxoEh75X+hVhGQdYNlIzbJSlqVCZi8XkWZrzvNgDypjZ-h2ciHkYs1tV1fVfUAS1jBtVZnVQLZGBbu5wkDdq967vuh6zm+H5fvE4y-saGlYSBWTgf5smhT5B2HEhIpOq67rFrhPoEf6upNfBkVUfANF0XCDFfEgzGAaxvbagC3FOXxrm9cJOJeeJAXHTJekJe4lTVMpQSiX++maUdcOndjClVEptQxmNFntdZ03dbN8BZTlnCQ-Nnlift4WHWBeNI8RAoXRRUXXSgzr-SgSUg8Np6Q3TzmcHlBUEcVcvCKBlWNjVYucWlI3nWziUSuTk2VjNc0Nf18GizeQ0AprwmjXrE0dYb1PG31A0DgA7Cs478Ez8ohQAYv0gx++IkhPNbuK+841tErxpLwHAB6oBAcgOoLt00CKHS8KDUHR9kxJx5oZAiAzgInJF8CKtnOpbXShiw0CJzw5zUGEJHcjWx49o3cLOFenhvpqvaZc0FqnFGCAD7wFMAjgKEaF8ZwIgggg7cAOptmca9bI6sBIOIwB+1sAcHDAEcnDHJJXD3boZxG1cAvsgxt1BJ9ByHUQoOHsonBSwa8CGeoqBMDx0HO0XUOA8DtS4lBDecJt4wF3psA+R8YBv04LieaRZIEoApB4XogdODDGXN+VcfAEwgjACAG4GArgjFIRoeW2AIBoC3EmC8Ww4CSHjs5TMzwmYACIAKjx1u4AR-AEyoPgGcVggRaacCYGgPQvAmYXw6O3dBaCP5hyEpgoS2DCh4D-v8E4SkpA3CICWF0aB-iqPAf8TQtcfyYz2gBZuyNgp8mEfzVOQs3R929PhVUXdoreK7v8BgjiaTbXpC49SQUtII10q3ZGV1fG3QCQPJ6ISRTyU8EAA
π» Code
Passing valid predicate types to
Array.filter
is an error in version 5.2.2.The following code produces errors in both version 5.1.6 and 5.2.2,
but the errors are are different and specify different locations.
In 5.1.6 the error is on
filter
, in 5.2.2 it is onmyPredicates
.π Actual behavior
Error, as shown above.
Also, the return type
(string|number)[]
is not correct - it should bestring[]|number[]
. I understand that the overly widened return type was thought to be better than nothing, However, since the new behavior in 5.2.2. is still buggy, perhaps the final fix could could also return the correct type.π Expected behavior
Emit no error.
Additional information about the issue
Note that using a template function does work with current 5.2.2 version:
However, the behavior described by passing the overloaded
myPredicate
template function as not the same is not the same as the behavior described by passingmyPredicateTpl
. Only the former describes the scenario such that the same type is used for all index values and that the array has single valued element type.We can actually generate a working version of this using the following distributed template function:
It also actually returns the correct type
string[] | number[]
.Here we can see it describes a union of signature types:
While currently TypeScript has the unfortunate limitation of only being able to return
string[] | number[]
,it may at some point be improved to overcome that limitation and return the accurate type. For that reason
it is better to encourage habitual use of a predicate description which actually describes the behavior of the predicate.
I guess putting a distributed template type like
ArrayFilterFunctionType
in a TypeScript lib file would be probably be too computationally expensive. However, if the same thing could be achieved with oneof -like notation and implementation, then it could become cheap enough.Finally, note that adding a catch all function with return type
never
to the overaloads will get rid of the error:However, it returns
(string|number)[]
, not the correct return typestring[] | number[]
.The text was updated successfully, but these errors were encountered: