Skip to content

Commit 9ef65ef

Browse files
committed
Use getSpreadArgumentType when relating to complex rest parameter types
1 parent 93c76cb commit 9ef65ef

File tree

1 file changed

+99
-95
lines changed

1 file changed

+99
-95
lines changed

src/compiler/checker.ts

Lines changed: 99 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -7789,8 +7789,12 @@ namespace ts {
77897789
}
77907790

77917791
function tryGetRestTypeOfSignature(signature: Signature): Type | undefined {
7792-
const type = getTypeOfRestParameter(signature);
7793-
return type && getIndexTypeOfType(type, IndexKind.Number);
7792+
if (signature.hasRestParameter) {
7793+
const sigRestType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]);
7794+
const restType = isTupleType(sigRestType) ? getRestTypeOfTupleType(sigRestType) : sigRestType;
7795+
return restType && getIndexTypeOfType(restType, IndexKind.Number);
7796+
}
7797+
return undefined;
77947798
}
77957799

77967800
function getSignatureInstantiation(signature: Signature, typeArguments: Type[] | undefined, isJavascript: boolean): Signature {
@@ -10718,9 +10722,9 @@ namespace ts {
1071810722
}
1071910723

1072010724
const sourceCount = getParameterCount(source);
10721-
const sourceGenericRestType = getGenericRestType(source);
10722-
const targetGenericRestType = sourceGenericRestType ? getGenericRestType(target) : undefined;
10723-
if (sourceGenericRestType && !(targetGenericRestType && sourceCount === targetCount)) {
10725+
const sourceRestType = getNonArrayRestType(source);
10726+
const targetRestType = sourceRestType ? getNonArrayRestType(target) : undefined;
10727+
if (sourceRestType && !(targetRestType && sourceCount === targetCount)) {
1072410728
return Ternary.False;
1072510729
}
1072610730

@@ -10749,8 +10753,8 @@ namespace ts {
1074910753
const paramCount = Math.max(sourceCount, targetCount);
1075010754
const lastIndex = paramCount - 1;
1075110755
for (let i = 0; i < paramCount; i++) {
10752-
const sourceType = i === lastIndex && sourceGenericRestType || getTypeAtPosition(source, i);
10753-
const targetType = i === lastIndex && targetGenericRestType || getTypeAtPosition(target, i);
10756+
const sourceType = i === lastIndex && sourceRestType || getTypeAtPosition(source, i);
10757+
const targetType = i === lastIndex && targetRestType || getTypeAtPosition(target, i);
1075410758
// In order to ensure that any generic type Foo<T> is at least co-variant with respect to T no matter
1075510759
// how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions,
1075610760
// they naturally relate only contra-variantly). However, if the source and target parameters both have
@@ -12693,6 +12697,11 @@ namespace ts {
1269312697
return type.target.hasRestElement ? type.typeArguments![type.target.typeParameters!.length - 1] : undefined;
1269412698
}
1269512699

12700+
function getRestArrayTypeOfTupleType(type: TupleTypeReference) {
12701+
const restType = getRestTypeOfTupleType(type);
12702+
return restType && createArrayType(restType);
12703+
}
12704+
1269612705
function getLengthOfTupleType(type: TupleTypeReference) {
1269712706
return getTypeReferenceArity(type) - (type.target.hasRestElement ? 1 : 0);
1269812707
}
@@ -13038,19 +13047,18 @@ namespace ts {
1303813047
function forEachMatchingParameterType(source: Signature, target: Signature, callback: (s: Type, t: Type) => void) {
1303913048
const sourceCount = getParameterCount(source);
1304013049
const targetCount = getParameterCount(target);
13041-
const sourceHasRest = hasEffectiveRestParameter(source);
13042-
const targetHasRest = hasEffectiveRestParameter(target);
13043-
const maxCount = sourceHasRest && targetHasRest ? Math.max(sourceCount, targetCount) :
13044-
sourceHasRest ? targetCount :
13045-
targetHasRest ? sourceCount :
13050+
const sourceRestType = getEffectiveRestType(source);
13051+
const targetRestType = getEffectiveRestType(target);
13052+
const maxCount = sourceRestType && targetRestType ? Math.max(sourceCount, targetCount) :
13053+
sourceRestType ? targetCount :
13054+
targetRestType ? sourceCount :
1304613055
Math.min(sourceCount, targetCount);
13047-
const targetGenericRestType = getGenericRestType(target);
13048-
const paramCount = targetGenericRestType ? Math.min(targetCount - 1, maxCount) : maxCount;
13056+
const paramCount = targetRestType ? Math.min(targetCount - 1, maxCount) : maxCount;
1304913057
for (let i = 0; i < paramCount; i++) {
1305013058
callback(getTypeAtPosition(source, i), getTypeAtPosition(target, i));
1305113059
}
13052-
if (targetGenericRestType) {
13053-
callback(getRestTypeAtPosition(source, paramCount), targetGenericRestType);
13060+
if (targetRestType) {
13061+
callback(getRestTypeAtPosition(source, paramCount), targetRestType);
1305413062
}
1305513063
}
1305613064

@@ -13510,32 +13518,37 @@ namespace ts {
1351013518
}
1351113519

1351213520
function inferFromProperties(source: Type, target: Type) {
13513-
if (isTupleType(source) && isTupleType(target)) {
13514-
const sourceLength = getLengthOfTupleType(source);
13515-
const targetLength = getLengthOfTupleType(target);
13516-
const sourceRestType = getRestTypeOfTupleType(source);
13517-
const targetRestType = getRestTypeOfTupleType(target);
13518-
const fixedLength = targetLength < sourceLength || sourceRestType ? targetLength : sourceLength;
13519-
for (let i = 0; i < fixedLength; i++) {
13520-
inferFromTypes(i < sourceLength ? source.typeArguments![i] : sourceRestType!, target.typeArguments![i]);
13521-
}
13522-
if (targetRestType) {
13523-
const types = fixedLength < sourceLength ? source.typeArguments!.slice(fixedLength, sourceLength) : [];
13524-
if (sourceRestType) {
13525-
types.push(sourceRestType);
13526-
}
13527-
if (types.length) {
13528-
inferFromTypes(getUnionType(types), targetRestType);
13521+
if (isTupleType(source)) {
13522+
if (isTupleType(target)) {
13523+
const sourceLength = getLengthOfTupleType(source);
13524+
const targetLength = getLengthOfTupleType(target);
13525+
const sourceRestType = getRestTypeOfTupleType(source);
13526+
const targetRestType = getRestTypeOfTupleType(target);
13527+
const fixedLength = targetLength < sourceLength || sourceRestType ? targetLength : sourceLength;
13528+
for (let i = 0; i < fixedLength; i++) {
13529+
inferFromTypes(i < sourceLength ? source.typeArguments![i] : sourceRestType!, target.typeArguments![i]);
13530+
}
13531+
if (targetRestType) {
13532+
const types = fixedLength < sourceLength ? source.typeArguments!.slice(fixedLength, sourceLength) : [];
13533+
if (sourceRestType) {
13534+
types.push(sourceRestType);
13535+
}
13536+
if (types.length) {
13537+
inferFromTypes(getUnionType(types), targetRestType);
13538+
}
1352913539
}
13540+
return;
13541+
}
13542+
if (isArrayType(target)) {
13543+
inferFromIndexTypes(source, target);
13544+
return;
1353013545
}
1353113546
}
13532-
else {
13533-
const properties = getPropertiesOfObjectType(target);
13534-
for (const targetProp of properties) {
13535-
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
13536-
if (sourceProp) {
13537-
inferFromTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp));
13538-
}
13547+
const properties = getPropertiesOfObjectType(target);
13548+
for (const targetProp of properties) {
13549+
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
13550+
if (sourceProp) {
13551+
inferFromTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp));
1353913552
}
1354013553
}
1354113554
}
@@ -18679,8 +18692,8 @@ namespace ts {
1867918692
inferTypes(context.inferences, thisArgumentType, thisType);
1868018693
}
1868118694

18682-
const genericRestType = getGenericRestType(signature);
18683-
const argCount = genericRestType ? Math.min(getParameterCount(signature) - 1, args.length) : args.length;
18695+
const restType = getNonArrayRestType(signature);
18696+
const argCount = restType ? Math.min(getParameterCount(signature) - 1, args.length) : args.length;
1868418697
for (let i = 0; i < argCount; i++) {
1868518698
const arg = args[i];
1868618699
if (arg.kind !== SyntaxKind.OmittedExpression) {
@@ -18693,14 +18706,21 @@ namespace ts {
1869318706
}
1869418707
}
1869518708

18696-
if (genericRestType) {
18697-
const spreadType = getSpreadArgumentType(args, argCount, args.length, genericRestType, context);
18698-
inferTypes(context.inferences, spreadType, genericRestType);
18709+
if (restType) {
18710+
const spreadType = getSpreadArgumentType(args, argCount, args.length, restType, context);
18711+
inferTypes(context.inferences, spreadType, restType);
1869918712
}
1870018713

1870118714
return getInferredTypes(context);
1870218715
}
1870318716

18717+
function getArrayifiedType(type: Type) {
18718+
if (forEachType(type, t => !(t.flags & (TypeFlags.Any | TypeFlags.Instantiable) || isArrayType(t) || isTupleType(t)))) {
18719+
return createArrayType(getIndexTypeOfType(type, IndexKind.Number) || errorType);
18720+
}
18721+
return type;
18722+
}
18723+
1870418724
function getSpreadArgumentType(args: ReadonlyArray<Expression>, index: number, argCount: number, restType: TypeParameter, context: InferenceContext | undefined) {
1870518725
if (index >= argCount - 1) {
1870618726
const arg = args[argCount - 1];
@@ -18709,7 +18729,7 @@ namespace ts {
1870918729
// and the argument are ...x forms.
1871018730
return arg.kind === SyntaxKind.SyntheticExpression ?
1871118731
createArrayType((<SyntheticExpression>arg).type) :
18712-
checkExpressionWithContextualType((<SpreadElement>arg).expression, restType, context);
18732+
getArrayifiedType(checkExpressionWithContextualType((<SpreadElement>arg).expression, restType, context));
1871318733
}
1871418734
}
1871518735
const contextualType = getIndexTypeOfType(restType, IndexKind.Number) || anyType;
@@ -18814,28 +18834,27 @@ namespace ts {
1881418834
}
1881518835
}
1881618836
const headMessage = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1;
18817-
const restIndex = signature.hasRestParameter ? signature.parameters.length - 1 : -1;
18818-
const restType = restIndex >= 0 ? getTypeOfSymbol(signature.parameters[restIndex]) : anyType;
18819-
for (let i = 0; i < args.length; i++) {
18837+
const restType = getNonArrayRestType(signature);
18838+
const argCount = restType ? Math.min(getParameterCount(signature) - 1, args.length) : args.length;
18839+
for (let i = 0; i < argCount; i++) {
1882018840
const arg = args[i];
1882118841
if (arg.kind !== SyntaxKind.OmittedExpression) {
18822-
if (i === restIndex && (restType.flags & TypeFlags.TypeParameter || isSpreadArgument(arg) && !isArrayType(restType))) {
18823-
const spreadType = getSpreadArgumentType(args, i, args.length, restType, /*context*/ undefined);
18824-
return checkTypeRelatedTo(spreadType, restType, relation, arg, headMessage);
18825-
}
18826-
else {
18827-
const paramType = getTypeAtPosition(signature, i);
18828-
const argType = checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
18829-
// If one or more arguments are still excluded (as indicated by a non-null excludeArgument parameter),
18830-
// we obtain the regular type of any object literal arguments because we may not have inferred complete
18831-
// parameter types yet and therefore excess property checks may yield false positives (see #17041).
18832-
const checkArgType = excludeArgument ? getRegularTypeOfObjectLiteral(argType) : argType;
18833-
if (!checkTypeRelatedTo(checkArgType, paramType, relation, reportErrors ? arg : undefined, headMessage)) {
18834-
return false;
18835-
}
18842+
const paramType = getTypeAtPosition(signature, i);
18843+
const argType = checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
18844+
// If one or more arguments are still excluded (as indicated by a non-null excludeArgument parameter),
18845+
// we obtain the regular type of any object literal arguments because we may not have inferred complete
18846+
// parameter types yet and therefore excess property checks may yield false positives (see #17041).
18847+
const checkArgType = excludeArgument ? getRegularTypeOfObjectLiteral(argType) : argType;
18848+
if (!checkTypeRelatedTo(checkArgType, paramType, relation, reportErrors ? arg : undefined, headMessage)) {
18849+
return false;
1883618850
}
1883718851
}
1883818852
}
18853+
if (restType) {
18854+
const spreadType = getSpreadArgumentType(args, argCount, args.length, restType, /*context*/ undefined);
18855+
const errorNode = reportErrors ? argCount < args.length ? args[argCount] : node : undefined;
18856+
return checkTypeRelatedTo(spreadType, restType, relation, errorNode, headMessage);
18857+
}
1883918858
return true;
1884018859
}
1884118860

@@ -19187,7 +19206,7 @@ namespace ts {
1918719206
checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJavaScriptFile(candidate.declaration));
1918819207
// If the original signature has a generic rest type, instantiation may produce a
1918919208
// signature with different arity and we need to perform another arity check.
19190-
if (getGenericRestType(candidate) && !hasCorrectArity(node, args, checkCandidate, signatureHelpTrailingComma)) {
19209+
if (getNonArrayRestType(candidate) && !hasCorrectArity(node, args, checkCandidate, signatureHelpTrailingComma)) {
1919119210
candidateForArgumentArityError = checkCandidate;
1919219211
continue;
1919319212
}
@@ -20138,14 +20157,11 @@ namespace ts {
2013820157

2013920158
function getRestTypeAtPosition(source: Signature, pos: number): Type {
2014020159
const paramCount = getParameterCount(source);
20141-
const hasRest = hasEffectiveRestParameter(source);
20142-
if (hasRest && pos === paramCount - 1) {
20143-
const genericRestType = getGenericRestType(source);
20144-
if (genericRestType) {
20145-
return genericRestType;
20146-
}
20160+
const restType = getEffectiveRestType(source);
20161+
if (restType && pos === paramCount - 1) {
20162+
return restType;
2014720163
}
20148-
const start = hasRest ? Math.min(pos, paramCount - 1) : pos;
20164+
const start = restType ? Math.min(pos, paramCount - 1) : pos;
2014920165
const types = [];
2015020166
const names = [];
2015120167
for (let i = start; i < paramCount; i++) {
@@ -20154,18 +20170,7 @@ namespace ts {
2015420170
}
2015520171
const minArgumentCount = getMinArgumentCount(source);
2015620172
const minLength = minArgumentCount < start ? 0 : minArgumentCount - start;
20157-
return createTupleType(types, minLength, hasRest, names);
20158-
}
20159-
20160-
function getTypeOfRestParameter(signature: Signature) {
20161-
if (signature.hasRestParameter) {
20162-
const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]);
20163-
if (isTupleType(restType)) {
20164-
return getRestTypeOfTupleType(restType);
20165-
}
20166-
return restType;
20167-
}
20168-
return undefined;
20173+
return createTupleType(types, minLength, !!restType, names);
2016920174
}
2017020175

2017120176
function getParameterCount(signature: Signature) {
@@ -20192,22 +20197,25 @@ namespace ts {
2019220197
return signature.minArgumentCount;
2019320198
}
2019420199

20195-
function getGenericRestType(signature: Signature) {
20200+
function hasEffectiveRestParameter(signature: Signature) {
2019620201
if (signature.hasRestParameter) {
2019720202
const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]);
20198-
if (restType.flags & TypeFlags.Instantiable) {
20199-
return restType;
20200-
}
20203+
return !isTupleType(restType) || restType.target.hasRestElement;
2020120204
}
20202-
return undefined;
20205+
return false;
2020320206
}
2020420207

20205-
function hasEffectiveRestParameter(signature: Signature) {
20208+
function getEffectiveRestType(signature: Signature) {
2020620209
if (signature.hasRestParameter) {
2020720210
const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]);
20208-
return !isTupleType(restType) || restType.target.hasRestElement;
20211+
return isTupleType(restType) ? getRestArrayTypeOfTupleType(restType) : restType;
2020920212
}
20210-
return false;
20213+
return undefined;
20214+
}
20215+
20216+
function getNonArrayRestType(signature: Signature) {
20217+
const restType = getEffectiveRestType(signature);
20218+
return restType && !isArrayType(restType) && !isTypeAny(restType) ? restType : undefined;
2021120219
}
2021220220

2021320221
function getTypeOfFirstParameterOfSignature(signature: Signature) {
@@ -21889,10 +21897,6 @@ namespace ts {
2188921897
}
2189021898
}
2189121899

21892-
function isRestParameterType(type: Type) {
21893-
return isArrayType(type) || isTupleType(type) || type.flags & TypeFlags.Instantiable && isTypeAssignableTo(type, anyArrayType);
21894-
}
21895-
2189621900
function checkParameter(node: ParameterDeclaration) {
2189721901
// Grammar checking
2189821902
// It is a SyntaxError if the Identifier "eval" or the Identifier "arguments" occurs as the
@@ -21924,7 +21928,7 @@ namespace ts {
2192421928

2192521929
// Only check rest parameter type if it's not a binding pattern. Since binding patterns are
2192621930
// not allowed in a rest parameter, we already have an error from checkGrammarParameterList.
21927-
if (node.dotDotDotToken && !isBindingPattern(node.name) && !isRestParameterType(getTypeOfSymbol(node.symbol))) {
21931+
if (node.dotDotDotToken && !isBindingPattern(node.name) && !isTypeAssignableTo(getTypeOfSymbol(node.symbol), anyArrayType)) {
2192821932
error(node, Diagnostics.A_rest_parameter_must_be_of_an_array_type);
2192921933
}
2193021934
}

0 commit comments

Comments
 (0)