-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Type inference for rest parameters does not work #41064
Comments
This is intended, as mentioned in #41048 (comment). Re-creating issues because you didn't like the response you got is icky. |
Thank you, I already know that the current behaviour is intended. I believe it is incorrect because This issue is about another problem, so it is not a re-creation. BTW, clothing issues without explaining is also not the right way to communicate. |
Another illustration: declare function f<T>(...list: T[]): T;
class A {
}
class B extends A {
}
class C extends A {
}
const t2 = f(new B(), new C(), null); TS says type of |
Generic inference proceeds along the following algorithm:
This is the intended behavior because calls like this declare function find<T>(haystack: T[], needle: T): boolean;
find([1, 2, 3], "four"); should fail; inferring Now if you write declare function find<T>(haystack: T[], needle: T): boolean;
const sn: Array<string | number> = [1, 2, 3];
find(sn, "four"); Now this is a valid call, because the candidate When you write something like this: declare function f<T>(...list: T[]): void;
const ar = ["a", "b", 0];
f(...ar); There's only one candidate presented here: declare function f<T>(...list: T[]): void;
const ar = ["a" as string | number];
f(...ar); which clearly must pass by your proposed logic of "they should behave the same", but the type of So anyway, this is the intended behavior; you haven't found a six-year-old bug in TypeScript. You're free to disagree with that design but it's not a bug. |
Thank you for the explanation, your time, an with all due respect to the six years of the TypeScript history, but... The system you outlined above is self-inconsistent and contradicts JavaScript semantic of the rest parameters (five years old feature). Consider the second example I gave, with three classes. In that example the types of the parameters are compatible and I would even be happy if the compiler deduces the parameter type Now, examples with two parameters are not very helpful, because they illustrate quite a different pattern, where two entitles are at play and there the compiler obtains the value for const array:(string|number)[] = ["a", 1]; do you? Why then when the very same array is created implicitly TypeScript says it's impossible? |
TS has many issues and their developers are so full of themselves in a silly way. So don't be too positive about it. |
Regarding the second example, I'm not sure what you're arguing. "Consistency" is a proxy measure of a useful system; there are calls we want to disallow and initializations we want to allow. The rules are designed to produce desired outcomes and this does sometimes mean that for two different ways of doing the same thing, only one will be an error. Honestly it doesn't sound like TypeScript is a good fit for how you'd like a type system to behave; I'd recommend Hegel if you want a JS-based type checker that prioritizes theoretical principles above human factors. |
@RyanCavanaugh, I don't think it's that simple. Here is an example: declare function f<T>(...list: T[]): T;
class A {
propA: number;
constructor() {
this.propA = 2;
}
}
class B extends A {
prop: number;
constructor() {
super();
this.prop = 1;
}
}
class C extends A {
f(){
}
}
const t3 = f(new B(), new C()); All three classes are non-empty, TypeScript thinks the call is invalid, but there is even a common non-empty base class. |
In a structural type system there's no theoretical reason to prefer inferring to |
I absolutely understand what type of errors/typos you want to catch.
But it already behaves like I want it to, with array initialisation.
Thank you. |
I'm arguing that the present set of decisions solves the problem at the wrong place. If you deduce |
Here is the sample: function g(...list: A[]):A {
console.log(list[0].propA);
return list[0];
}
function h<T extends B|C>(...list: T[]):T {
console.log(list[0].propA);
return list[0];
}
const t3 = f(new B(), new C()); // error
const t4 = g(new B(), new C()); // OK
const t5 = h(new B(), new C()); // error |
The failure mode of this was addressed by the Ultimately generic inference is an inference process that produces the type we think you wanted based on available information. Explicit type arguments exist for a reason -- there are cases where that inference is overspecific and you need to supply the "this is what I mean to have happen" |
I can't understand why do you keep referring to the find example when it works and fails in a different way comparing to the case I report.
But then I have to care about this line if argument types get an update, and if the union of argument types shrinks, the compiler will not tell me that my manual type is too wide. In this particular case I would rather use an explicit array:
Seems like there is a difference between those functions, otherwise no study would be required. I still believe you correct the error at the wrong place. TypeScript provides |
This is begging the question. Your assertion has been that a
We're just going to have to agree to disagree here. This is basically the same as #27859 so you can add your use case there for tracking purposes, but I don't think we're going to completely rethink inference based on this one use case. |
But heterogeneous arrays are already accepted by TypeScript, and given that if you process |
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
TypeScript Version: 4.1.0-dev.20201012
Search Terms: type inference, rest parameters
Consider the following function declaration:
Expected behavior: The function
f()
can receive arbitrary number of arguments of any type. For the given list of arguments typeT
should be set to the "least common denominator" of the argument types.Actual behavior: TS fails to infer that common type by itself, but is happy to consume the same call when the common type is provided manually:
The other case that works is when arguments contains the
any
type:Code
Output
Compiler Options
Playground Link: Provided
The text was updated successfully, but these errors were encountered: