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

Type inference for rest parameters differs from that for arrays #41048

Closed
ezsh opened this issue Oct 11, 2020 · 7 comments
Closed

Type inference for rest parameters differs from that for arrays #41048

ezsh opened this issue Oct 11, 2020 · 7 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@ezsh
Copy link

ezsh commented Oct 11, 2020

TypeScript Version: 4.1.0-dev.20201011

Search Terms: rest parameters, type inference

Expected behavior: All test cases are valid ones.

Actual behavior: cases t3 and t4 are correctly understood by the TS compiler, the other ones result in errors:

Argument of type 'number' is not assignable to parameter of type 'string'.
Argument of type '"b"' is not assignable to parameter of type '"a"'.

Related Issues: #37193

Code

declare function f1<T>(...list: ReadonlyArray<T>): T;
declare function f2<T>(...list: T[]): T;
declare function f3<T>(list: T[]): T;

const s:string = "str";
const ar = ["a", "b", "c", 0];

const t1 = f1(s, "a", "b", 0);
const t2 = f2("a", "b", "c", 0);
const t3 = f3(["a", "b", "c", 0]);
const t4 = f1(...ar);
Output
"use strict";
const s = "str";
const ar = ["a", "b", "c", 0];
const t1 = f1(s, "a", "b", 0);
const t2 = f2("a", "b", "c", 0);
const t3 = f3(["a", "b", "c", 0]);
const t4 = f1(...ar);
Compiler Options
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "declaration": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "moduleResolution": 2,
    "target": "ES2017",
    "jsx": "React",
    "module": "ESNext"
  }
}

Playground Link: Provided

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Oct 12, 2020
@RyanCavanaugh
Copy link
Member

RyanCavanaugh commented Oct 12, 2020

These are different syntaxes and thus mean different things; by having different syntax do different things you are then able to express different behavior depending on which you want to happen. Changing either one of these behaviors would be a fairly large breaking change and would need to be justified by something more than "it's different".

@ezsh
Copy link
Author

ezsh commented Oct 12, 2020

I don't think it works as intended: the t1 case is failing because TS deduces the T type from the first argument only, while it has to use the whole arguments array. For the t2 case the type of T is inferred as "a", which is obviously useless and wrong.

@RyanCavanaugh
Copy link
Member

This is the intended behavior. In t2, inference failed to produce a suitable result, so the call was processed according to the first available candidate, which was "a".

@ezsh
Copy link
Author

ezsh commented Oct 12, 2020

I can't agree with you. MDN states: "A function's last parameter can be prefixed with ... which will cause all remaining (user supplied) arguments to be placed within a "standard" JavaScript array."

But this is not the case with TS: for rest parameters it fails to deduce the same union type as it does for array. I'd expect it to infer T to be string|number for t1 and "a" | "b" | "c" | number for t2, right as it does that for the ar declaration.

@ezsh
Copy link
Author

ezsh commented Oct 12, 2020

And in any case, all four examples are valid, if TS can't (or does not want to) infer the correct union type, it should have set T to any instead of signalling errors.

@RyanCavanaugh
Copy link
Member

"valid" and "correct" are outcomes decided by the type system, and the type system has intentionally been designed with flagging some of these as errors as not others.

@ezsh
Copy link
Author

ezsh commented Oct 12, 2020

The type system is wrong then: semantically rest parameters do not differ from arrays is JS, while TS assign them different behaviour. The t4 case is semantically identical to the t1, but TS handles them differently, which is incorrect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

2 participants