diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index dfd479127202c..8295a3d1ba707 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -12576,8 +12576,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
type = tryGetTypeFromEffectiveTypeNode(declaration) || checkObjectLiteralMethod(declaration, CheckMode.Normal);
}
else if (
- isParameter(declaration)
- || isPropertyDeclaration(declaration)
+ isPropertyDeclaration(declaration)
|| isPropertySignature(declaration)
|| isVariableDeclaration(declaration)
|| isBindingElement(declaration)
@@ -12585,6 +12584,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
) {
type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true);
}
+ else if (isParameter(declaration)) {
+ type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true);
+ if (declaration.dotDotDotToken) {
+ type = normalizeNoInferSpread(type);
+ }
+ }
// getTypeOfSymbol dispatches some JS merges incorrectly because their symbol flags are not mutually exclusive.
// Re-dispatch based on valueDeclaration.kind instead.
else if (isEnumDeclaration(declaration)) {
@@ -12808,7 +12813,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function getTypeOfInstantiatedSymbol(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
- return links.type || (links.type = instantiateType(getTypeOfSymbol(links.target!), links.mapper));
+ if (!links.type) {
+ const declaration = links.target!.valueDeclaration;
+ let type = instantiateType(getTypeOfSymbol(links.target!), links.mapper);
+ if (declaration && isParameter(declaration) && declaration.dotDotDotToken) {
+ type = normalizeNoInferSpread(type);
+ }
+ links.type = type;
+ }
+ return links.type;
}
function getWriteTypeOfInstantiatedSymbol(symbol: Symbol): Type {
@@ -17866,6 +17879,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return createTypeReference(target, elementTypes);
}
if (target.combinedFlags & ElementFlags.Variadic) {
+ elementTypes = sameMap(elementTypes, (t, i) => {
+ return target.elementFlags[i] & ElementFlags.Variadic ? normalizeNoInferSpread(t) : t;
+ });
// Transform [A, ...(X | Y | Z)] into [A, ...X] | [A, ...Y] | [A, ...Z]
const unionIndex = findIndex(elementTypes, (t, i) => !!(target.elementFlags[i] & ElementFlags.Variadic && t.flags & (TypeFlags.Never | TypeFlags.Union)));
if (unionIndex >= 0) {
@@ -17952,6 +17968,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
+ function normalizeNoInferSpread(type: Type): Type {
+ if (!isNoInferType(type)) {
+ return type;
+ }
+ return mapType((type as SubstitutionType).baseType, t => {
+ if (!isTupleType(t)) {
+ return getNoInferType(type);
+ }
+ return createTupleType(
+ map(getElementTypes(t), getNoInferType),
+ t.target.elementFlags,
+ t.target.readonly,
+ t.target.labeledElementDeclarations,
+ );
+ });
+ }
+
function sliceTupleType(type: TupleTypeReference, index: number, endSkipCount = 0) {
const target = type.target;
const endIndex = getTypeReferenceArity(type) - endSkipCount;
diff --git a/tests/baselines/reference/noInferRestSpread1.errors.txt b/tests/baselines/reference/noInferRestSpread1.errors.txt
new file mode 100644
index 0000000000000..21f67ac0e0a60
--- /dev/null
+++ b/tests/baselines/reference/noInferRestSpread1.errors.txt
@@ -0,0 +1,41 @@
+noInferRestSpread1.ts(7,22): error TS2345: Argument of type '(a: number, b: number) => void' is not assignable to parameter of type '(args_0: number) => void'.
+ Target signature provides too few arguments. Expected 2 or more, but got 1.
+
+
+==== noInferRestSpread1.ts (1 errors) ====
+ declare function call(
+ arg: (...args: NoInfer) => void,
+ ...args: A
+ ): A;
+
+ const result1 = call((a: number) => {}, 1, 2);
+ const result2 = call((a: number, b: number) => {}, 1); // error
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+!!! error TS2345: Argument of type '(a: number, b: number) => void' is not assignable to parameter of type '(args_0: number) => void'.
+!!! error TS2345: Target signature provides too few arguments. Expected 2 or more, but got 1.
+ const result3 = call((a) => {}, 1, ''); // test contextual parameters
+ const result4 = call((a, b) => {}, 1, ''); // test contextual parameters
+ const result5 = call((...args) => {}, 1, ''); // test contextual parameters
+ const result6 = call((a, ...rest) => {}, 1, ''); // test contextual parameters
+
+ declare function fn1(
+ cb: (a: [number, ...NoInfer]) => void,
+ args: A,
+ ): A;
+
+ declare const singleStr: [string];
+
+ const result7 = fn1((arg) => {
+ arg.length;
+ }, singleStr);
+
+ declare const tupleUnion: [string] | [number, boolean];
+
+ const result8 = fn1((arg) => {
+ arg.length;
+ }, tupleUnion);
+
+ declare function fn2(arg: (...args: NoInfer<[string, number]>) => void): void;
+
+ fn2((a, ...rest) => {});
+
\ No newline at end of file
diff --git a/tests/baselines/reference/noInferRestSpread1.symbols b/tests/baselines/reference/noInferRestSpread1.symbols
new file mode 100644
index 0000000000000..18dc968f1abc5
--- /dev/null
+++ b/tests/baselines/reference/noInferRestSpread1.symbols
@@ -0,0 +1,113 @@
+//// [tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts] ////
+
+=== noInferRestSpread1.ts ===
+declare function call(
+>call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0))
+>A : Symbol(A, Decl(noInferRestSpread1.ts, 0, 22))
+
+ arg: (...args: NoInfer) => void,
+>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 0, 52))
+>args : Symbol(args, Decl(noInferRestSpread1.ts, 1, 8))
+>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
+>A : Symbol(A, Decl(noInferRestSpread1.ts, 0, 22))
+
+ ...args: A
+>args : Symbol(args, Decl(noInferRestSpread1.ts, 1, 37))
+>A : Symbol(A, Decl(noInferRestSpread1.ts, 0, 22))
+
+): A;
+>A : Symbol(A, Decl(noInferRestSpread1.ts, 0, 22))
+
+const result1 = call((a: number) => {}, 1, 2);
+>result1 : Symbol(result1, Decl(noInferRestSpread1.ts, 5, 5))
+>call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0))
+>a : Symbol(a, Decl(noInferRestSpread1.ts, 5, 22))
+
+const result2 = call((a: number, b: number) => {}, 1); // error
+>result2 : Symbol(result2, Decl(noInferRestSpread1.ts, 6, 5))
+>call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0))
+>a : Symbol(a, Decl(noInferRestSpread1.ts, 6, 22))
+>b : Symbol(b, Decl(noInferRestSpread1.ts, 6, 32))
+
+const result3 = call((a) => {}, 1, ''); // test contextual parameters
+>result3 : Symbol(result3, Decl(noInferRestSpread1.ts, 7, 5))
+>call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0))
+>a : Symbol(a, Decl(noInferRestSpread1.ts, 7, 22))
+
+const result4 = call((a, b) => {}, 1, ''); // test contextual parameters
+>result4 : Symbol(result4, Decl(noInferRestSpread1.ts, 8, 5))
+>call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0))
+>a : Symbol(a, Decl(noInferRestSpread1.ts, 8, 22))
+>b : Symbol(b, Decl(noInferRestSpread1.ts, 8, 24))
+
+const result5 = call((...args) => {}, 1, ''); // test contextual parameters
+>result5 : Symbol(result5, Decl(noInferRestSpread1.ts, 9, 5))
+>call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0))
+>args : Symbol(args, Decl(noInferRestSpread1.ts, 9, 22))
+
+const result6 = call((a, ...rest) => {}, 1, ''); // test contextual parameters
+>result6 : Symbol(result6, Decl(noInferRestSpread1.ts, 10, 5))
+>call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0))
+>a : Symbol(a, Decl(noInferRestSpread1.ts, 10, 22))
+>rest : Symbol(rest, Decl(noInferRestSpread1.ts, 10, 24))
+
+declare function fn1(
+>fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 10, 48))
+>A : Symbol(A, Decl(noInferRestSpread1.ts, 12, 21))
+
+ cb: (a: [number, ...NoInfer]) => void,
+>cb : Symbol(cb, Decl(noInferRestSpread1.ts, 12, 42))
+>a : Symbol(a, Decl(noInferRestSpread1.ts, 13, 7))
+>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
+>A : Symbol(A, Decl(noInferRestSpread1.ts, 12, 21))
+
+ args: A,
+>args : Symbol(args, Decl(noInferRestSpread1.ts, 13, 43))
+>A : Symbol(A, Decl(noInferRestSpread1.ts, 12, 21))
+
+): A;
+>A : Symbol(A, Decl(noInferRestSpread1.ts, 12, 21))
+
+declare const singleStr: [string];
+>singleStr : Symbol(singleStr, Decl(noInferRestSpread1.ts, 17, 13))
+
+const result7 = fn1((arg) => {
+>result7 : Symbol(result7, Decl(noInferRestSpread1.ts, 19, 5))
+>fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 10, 48))
+>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 19, 21))
+
+ arg.length;
+>arg.length : Symbol(length)
+>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 19, 21))
+>length : Symbol(length)
+
+}, singleStr);
+>singleStr : Symbol(singleStr, Decl(noInferRestSpread1.ts, 17, 13))
+
+declare const tupleUnion: [string] | [number, boolean];
+>tupleUnion : Symbol(tupleUnion, Decl(noInferRestSpread1.ts, 23, 13))
+
+const result8 = fn1((arg) => {
+>result8 : Symbol(result8, Decl(noInferRestSpread1.ts, 25, 5))
+>fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 10, 48))
+>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 25, 21))
+
+ arg.length;
+>arg.length : Symbol(length)
+>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 25, 21))
+>length : Symbol(length)
+
+}, tupleUnion);
+>tupleUnion : Symbol(tupleUnion, Decl(noInferRestSpread1.ts, 23, 13))
+
+declare function fn2(arg: (...args: NoInfer<[string, number]>) => void): void;
+>fn2 : Symbol(fn2, Decl(noInferRestSpread1.ts, 27, 15))
+>arg : Symbol(arg, Decl(noInferRestSpread1.ts, 29, 21))
+>args : Symbol(args, Decl(noInferRestSpread1.ts, 29, 27))
+>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
+
+fn2((a, ...rest) => {});
+>fn2 : Symbol(fn2, Decl(noInferRestSpread1.ts, 27, 15))
+>a : Symbol(a, Decl(noInferRestSpread1.ts, 31, 5))
+>rest : Symbol(rest, Decl(noInferRestSpread1.ts, 31, 7))
+
diff --git a/tests/baselines/reference/noInferRestSpread1.types b/tests/baselines/reference/noInferRestSpread1.types
new file mode 100644
index 0000000000000..6ff3b0dbaf7ef
--- /dev/null
+++ b/tests/baselines/reference/noInferRestSpread1.types
@@ -0,0 +1,211 @@
+//// [tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts] ////
+
+=== noInferRestSpread1.ts ===
+declare function call(
+>call : (arg: (...args: NoInfer) => void, ...args: A) => A
+> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ ^^ ^^^^^
+
+ arg: (...args: NoInfer) => void,
+>arg : (...args: NoInfer) => void
+> : ^^^^ ^^ ^^^^^
+>args : NoInfer
+> : ^^^^^^^^^^
+
+ ...args: A
+>args : A
+> : ^
+
+): A;
+
+const result1 = call((a: number) => {}, 1, 2);
+>result1 : [number, number]
+> : ^^^^^^^^^^^^^^^^
+>call((a: number) => {}, 1, 2) : [number, number]
+> : ^^^^^^^^^^^^^^^^
+>call : (arg: (...args: NoInfer) => void, ...args: A) => A
+> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ ^^ ^^^^^
+>(a: number) => {} : (a: number) => void
+> : ^ ^^ ^^^^^^^^^
+>a : number
+> : ^^^^^^
+>1 : 1
+> : ^
+>2 : 2
+> : ^
+
+const result2 = call((a: number, b: number) => {}, 1); // error
+>result2 : [number]
+> : ^^^^^^^^
+>call((a: number, b: number) => {}, 1) : [number]
+> : ^^^^^^^^
+>call : (arg: (...args: NoInfer) => void, ...args: A) => A
+> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ ^^ ^^^^^
+>(a: number, b: number) => {} : (a: number, b: number) => void
+> : ^ ^^ ^^ ^^ ^^^^^^^^^
+>a : number
+> : ^^^^^^
+>b : number
+> : ^^^^^^
+>1 : 1
+> : ^
+
+const result3 = call((a) => {}, 1, ''); // test contextual parameters
+>result3 : [number, string]
+> : ^^^^^^^^^^^^^^^^
+>call((a) => {}, 1, '') : [number, string]
+> : ^^^^^^^^^^^^^^^^
+>call : (arg: (...args: NoInfer) => void, ...args: A) => A
+> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ ^^ ^^^^^
+>(a) => {} : (a: number) => void
+> : ^ ^^^^^^^^^^^^^^^^^
+>a : number
+> : ^^^^^^
+>1 : 1
+> : ^
+>'' : ""
+> : ^^
+
+const result4 = call((a, b) => {}, 1, ''); // test contextual parameters
+>result4 : [number, string]
+> : ^^^^^^^^^^^^^^^^
+>call((a, b) => {}, 1, '') : [number, string]
+> : ^^^^^^^^^^^^^^^^
+>call : (arg: (...args: NoInfer) => void, ...args: A) => A
+> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ ^^ ^^^^^
+>(a, b) => {} : (a: number, b: string) => void
+> : ^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
+>a : number
+> : ^^^^^^
+>b : string
+> : ^^^^^^
+>1 : 1
+> : ^
+>'' : ""
+> : ^^
+
+const result5 = call((...args) => {}, 1, ''); // test contextual parameters
+>result5 : [number, string]
+> : ^^^^^^^^^^^^^^^^
+>call((...args) => {}, 1, '') : [number, string]
+> : ^^^^^^^^^^^^^^^^
+>call : (arg: (...args: NoInfer) => void, ...args: A) => A
+> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ ^^ ^^^^^
+>(...args) => {} : (args_0: number, args_1: string) => void
+> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>args : [number, string]
+> : ^^^^^^^^^^^^^^^^
+>1 : 1
+> : ^
+>'' : ""
+> : ^^
+
+const result6 = call((a, ...rest) => {}, 1, ''); // test contextual parameters
+>result6 : [number, string]
+> : ^^^^^^^^^^^^^^^^
+>call((a, ...rest) => {}, 1, '') : [number, string]
+> : ^^^^^^^^^^^^^^^^
+>call : (arg: (...args: NoInfer) => void, ...args: A) => A
+> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ ^^ ^^^^^
+>(a, ...rest) => {} : (a: number, rest_0: string) => void
+> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>a : number
+> : ^^^^^^
+>rest : [string]
+> : ^^^^^^^^
+>1 : 1
+> : ^
+>'' : ""
+> : ^^
+
+declare function fn1(
+>fn1 : (cb: (a: [number, ...NoInfer]) => void, args: A) => A
+> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^
+
+ cb: (a: [number, ...NoInfer]) => void,
+>cb : (a: [number, ...NoInfer]) => void
+> : ^ ^^ ^^^^^
+>a : [number, ...NoInfer]
+> : ^^^^^^^^^^^^^^^^^^^^^^^
+
+ args: A,
+>args : A
+> : ^
+
+): A;
+
+declare const singleStr: [string];
+>singleStr : [string]
+> : ^^^^^^^^
+
+const result7 = fn1((arg) => {
+>result7 : [string]
+> : ^^^^^^^^
+>fn1((arg) => { arg.length;}, singleStr) : [string]
+> : ^^^^^^^^
+>fn1 : (cb: (a: [number, ...NoInfer]) => void, args: A) => A
+> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^
+>(arg) => { arg.length;} : (arg: [number, string]) => void
+> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>arg : [number, string]
+> : ^^^^^^^^^^^^^^^^
+
+ arg.length;
+>arg.length : 2
+> : ^
+>arg : [number, string]
+> : ^^^^^^^^^^^^^^^^
+>length : 2
+> : ^
+
+}, singleStr);
+>singleStr : [string]
+> : ^^^^^^^^
+
+declare const tupleUnion: [string] | [number, boolean];
+>tupleUnion : [string] | [number, boolean]
+> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+const result8 = fn1((arg) => {
+>result8 : [string] | [number, boolean]
+> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>fn1((arg) => { arg.length;}, tupleUnion) : [string] | [number, boolean]
+> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>fn1 : (cb: (a: [number, ...NoInfer]) => void, args: A) => A
+> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^
+>(arg) => { arg.length;} : (arg: [number, string] | [number, number, boolean]) => void
+> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>arg : [number, string] | [number, number, boolean]
+> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ arg.length;
+>arg.length : 2 | 3
+> : ^^^^^
+>arg : [number, string] | [number, number, boolean]
+> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>length : 2 | 3
+> : ^^^^^
+
+}, tupleUnion);
+>tupleUnion : [string] | [number, boolean]
+> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+declare function fn2(arg: (...args: NoInfer<[string, number]>) => void): void;
+>fn2 : (arg: (...args: NoInfer<[string, number]>) => void) => void
+> : ^ ^^ ^^^^^
+>arg : (args_0: string, args_1: number) => void
+> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>args : [string, number]
+> : ^^^^^^^^^^^^^^^^
+
+fn2((a, ...rest) => {});
+>fn2((a, ...rest) => {}) : void
+> : ^^^^
+>fn2 : (arg: (...args: NoInfer<[string, number]>) => void) => void
+> : ^ ^^ ^^^^^
+>(a, ...rest) => {} : (a: string, rest_0: number) => void
+> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>a : string
+> : ^^^^^^
+>rest : [number]
+> : ^^^^^^^^
+
diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts b/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts
new file mode 100644
index 0000000000000..c86f2ec235a8d
--- /dev/null
+++ b/tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts
@@ -0,0 +1,35 @@
+// @strict: true
+// @noEmit: true
+
+declare function call(
+ arg: (...args: NoInfer) => void,
+ ...args: A
+): A;
+
+const result1 = call((a: number) => {}, 1, 2);
+const result2 = call((a: number, b: number) => {}, 1); // error
+const result3 = call((a) => {}, 1, ''); // test contextual parameters
+const result4 = call((a, b) => {}, 1, ''); // test contextual parameters
+const result5 = call((...args) => {}, 1, ''); // test contextual parameters
+const result6 = call((a, ...rest) => {}, 1, ''); // test contextual parameters
+
+declare function fn1(
+ cb: (a: [number, ...NoInfer]) => void,
+ args: A,
+): A;
+
+declare const singleStr: [string];
+
+const result7 = fn1((arg) => {
+ arg.length;
+}, singleStr);
+
+declare const tupleUnion: [string] | [number, boolean];
+
+const result8 = fn1((arg) => {
+ arg.length;
+}, tupleUnion);
+
+declare function fn2(arg: (...args: NoInfer<[string, number]>) => void): void;
+
+fn2((a, ...rest) => {});