Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12576,15 +12576,20 @@ 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)
|| isJSDocPropertyLikeTag(declaration)
) {
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)) {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
41 changes: 41 additions & 0 deletions tests/baselines/reference/noInferRestSpread1.errors.txt
Original file line number Diff line number Diff line change
@@ -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<A extends readonly unknown[]>(
arg: (...args: NoInfer<A>) => 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<A extends unknown[]>(
cb: (a: [number, ...NoInfer<A>]) => 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) => {});

113 changes: 113 additions & 0 deletions tests/baselines/reference/noInferRestSpread1.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//// [tests/cases/conformance/types/typeRelationships/typeInference/noInferRestSpread1.ts] ////

=== noInferRestSpread1.ts ===
declare function call<A extends readonly unknown[]>(
>call : Symbol(call, Decl(noInferRestSpread1.ts, 0, 0))
>A : Symbol(A, Decl(noInferRestSpread1.ts, 0, 22))

arg: (...args: NoInfer<A>) => 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<A extends unknown[]>(
>fn1 : Symbol(fn1, Decl(noInferRestSpread1.ts, 10, 48))
>A : Symbol(A, Decl(noInferRestSpread1.ts, 12, 21))

cb: (a: [number, ...NoInfer<A>]) => 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))

Loading