From d6b2dd5d8e94ad5697377f2951914125c2e37c8e Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Sun, 16 May 2021 11:32:43 -0700
Subject: [PATCH 1/3] Move all getIndexedAccessType flags to AccessFlags enum

---
 src/compiler/checker.ts | 74 ++++++++++++++++++-----------------------
 src/compiler/types.ts   | 22 ++++++++----
 2 files changed, 48 insertions(+), 48 deletions(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 576c055762f85..60f8a0f6fa881 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -180,15 +180,6 @@ namespace ts {
         IsForSignatureHelp = 1 << 4,    // Call resolution for purposes of signature help
     }
 
-    const enum AccessFlags {
-        None = 0,
-        NoIndexSignatures = 1 << 0,
-        Writing = 1 << 1,
-        CacheSymbol = 1 << 2,
-        NoTupleBoundsCheck = 1 << 3,
-        ExpressionPosition = 1 << 4,
-    }
-
     const enum SignatureCheckMode {
         BivariantCallback = 1 << 0,
         StrictCallback    = 1 << 1,
@@ -8317,7 +8308,7 @@ namespace ts {
                     // Use explicitly specified property name ({ p: xxx } form), or otherwise the implied name ({ p } form)
                     const name = declaration.propertyName || <Identifier>declaration.name;
                     const indexType = getLiteralTypeFromPropertyName(name);
-                    const declaredType = getIndexedAccessType(parentType, indexType, /*noUncheckedIndexedAccessCandidate*/ undefined, name, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, AccessFlags.ExpressionPosition);
+                    const declaredType = getIndexedAccessType(parentType, indexType, AccessFlags.ExpressionPosition, name);
                     type = getFlowTypeOfDestructuring(declaration, declaredType);
                 }
             }
@@ -8337,8 +8328,8 @@ namespace ts {
                 }
                 else if (isArrayLikeType(parentType)) {
                     const indexType = getLiteralType(index);
-                    const accessFlags = hasDefaultValue(declaration) ? AccessFlags.NoTupleBoundsCheck : 0;
-                    const declaredType = getIndexedAccessTypeOrUndefined(parentType, indexType, /*noUncheckedIndexedAccessCandidate*/ undefined, declaration.name, accessFlags | AccessFlags.ExpressionPosition) || errorType;
+                    const accessFlags = AccessFlags.ExpressionPosition | (hasDefaultValue(declaration) ? AccessFlags.NoTupleBoundsCheck : 0);
+                    const declaredType = getIndexedAccessTypeOrUndefined(parentType, indexType, accessFlags, declaration.name) || errorType;
                     type = getFlowTypeOfDestructuring(declaration, declaredType);
                 }
                 else {
@@ -11400,14 +11391,14 @@ namespace ts {
         function getConstraintFromIndexedAccess(type: IndexedAccessType) {
             const indexConstraint = getSimplifiedTypeOrConstraint(type.indexType);
             if (indexConstraint && indexConstraint !== type.indexType) {
-                const indexedAccess = getIndexedAccessTypeOrUndefined(type.objectType, indexConstraint, type.noUncheckedIndexedAccessCandidate);
+                const indexedAccess = getIndexedAccessTypeOrUndefined(type.objectType, indexConstraint, type.accessFlags);
                 if (indexedAccess) {
                     return indexedAccess;
                 }
             }
             const objectConstraint = getSimplifiedTypeOrConstraint(type.objectType);
             if (objectConstraint && objectConstraint !== type.objectType) {
-                return getIndexedAccessTypeOrUndefined(objectConstraint, type.indexType, type.noUncheckedIndexedAccessCandidate);
+                return getIndexedAccessTypeOrUndefined(objectConstraint, type.indexType, type.accessFlags);
             }
             return undefined;
         }
@@ -11613,7 +11604,7 @@ namespace ts {
                 if (t.flags & TypeFlags.IndexedAccess) {
                     const baseObjectType = getBaseConstraint((<IndexedAccessType>t).objectType);
                     const baseIndexType = getBaseConstraint((<IndexedAccessType>t).indexType);
-                    const baseIndexedAccess = baseObjectType && baseIndexType && getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType, (<IndexedAccessType>t).noUncheckedIndexedAccessCandidate);
+                    const baseIndexedAccess = baseObjectType && baseIndexType && getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType, (<IndexedAccessType>t).accessFlags);
                     return baseIndexedAccess && getBaseConstraint(baseIndexedAccess);
                 }
                 if (t.flags & TypeFlags.Conditional) {
@@ -14554,13 +14545,13 @@ namespace ts {
             return result;
         }
 
-        function createIndexedAccessType(objectType: Type, indexType: Type, aliasSymbol: Symbol | undefined, aliasTypeArguments: readonly Type[] | undefined, shouldIncludeUndefined: boolean) {
+        function createIndexedAccessType(objectType: Type, indexType: Type, accessFlags: AccessFlags, aliasSymbol: Symbol | undefined, aliasTypeArguments: readonly Type[] | undefined) {
             const type = <IndexedAccessType>createType(TypeFlags.IndexedAccess);
             type.objectType = objectType;
             type.indexType = indexType;
+            type.accessFlags = accessFlags;
             type.aliasSymbol = aliasSymbol;
             type.aliasTypeArguments = aliasTypeArguments;
-            type.noUncheckedIndexedAccessCandidate = shouldIncludeUndefined;
             return type;
         }
 
@@ -14613,14 +14604,14 @@ namespace ts {
             return true;
         }
 
-        function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, fullIndexType: Type, suppressNoImplicitAnyError: boolean, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags, noUncheckedIndexedAccessCandidate?: boolean, reportDeprecated?: boolean) {
+        function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, fullIndexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags) {
             const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
             const propName = accessNode && isPrivateIdentifier(accessNode) ? undefined : getPropertyNameFromIndex(indexType, accessNode);
 
             if (propName !== undefined) {
                 const prop = getPropertyOfType(objectType, propName);
                 if (prop) {
-                    if (reportDeprecated && accessNode && prop.declarations && getDeclarationNodeFlagsFromSymbol(prop) & NodeFlags.Deprecated && isUncalledFunctionReference(accessNode, prop)) {
+                    if (accessFlags & AccessFlags.ReportDeprecated && accessNode && prop.declarations && getDeclarationNodeFlagsFromSymbol(prop) & NodeFlags.Deprecated && isUncalledFunctionReference(accessNode, prop)) {
                         const deprecatedNode = accessExpression?.argumentExpression ?? (isIndexedAccessTypeNode(accessNode) ? accessNode.indexType : accessNode);
                         addDeprecatedSuggestion(deprecatedNode, prop.declarations, propName as string);
                     }
@@ -14656,7 +14647,7 @@ namespace ts {
                     errorIfWritingToReadonlyIndex(getIndexInfoOfType(objectType, IndexKind.Number));
                     return mapType(objectType, t => {
                         const restType = getRestTypeOfTupleType(<TupleTypeReference>t) || undefinedType;
-                        return noUncheckedIndexedAccessCandidate ? getUnionType([restType, undefinedType]) : restType;
+                        return accessFlags & AccessFlags.IncludeUndefined ? getUnionType([restType, undefinedType]) : restType;
                     });
                 }
             }
@@ -14676,10 +14667,10 @@ namespace ts {
                     if (accessNode && !isTypeAssignableToKind(indexType, TypeFlags.String | TypeFlags.Number)) {
                         const indexNode = getIndexNodeForAccessExpression(accessNode);
                         error(indexNode, Diagnostics.Type_0_cannot_be_used_as_an_index_type, typeToString(indexType));
-                        return noUncheckedIndexedAccessCandidate ? getUnionType([indexInfo.type, undefinedType]) : indexInfo.type;
+                        return accessFlags & AccessFlags.IncludeUndefined ? getUnionType([indexInfo.type, undefinedType]) : indexInfo.type;
                     }
                     errorIfWritingToReadonlyIndex(indexInfo);
-                    return noUncheckedIndexedAccessCandidate ? getUnionType([indexInfo.type, undefinedType]) : indexInfo.type;
+                    return accessFlags & AccessFlags.IncludeUndefined ? getUnionType([indexInfo.type, undefinedType]) : indexInfo.type;
                 }
                 if (indexType.flags & TypeFlags.Never) {
                     return neverType;
@@ -14704,7 +14695,7 @@ namespace ts {
                     if (objectType.symbol === globalThisSymbol && propName !== undefined && globalThisSymbol.exports!.has(propName) && (globalThisSymbol.exports!.get(propName)!.flags & SymbolFlags.BlockScoped)) {
                         error(accessExpression, Diagnostics.Property_0_does_not_exist_on_type_1, unescapeLeadingUnderscores(propName), typeToString(objectType));
                     }
-                    else if (noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors && !suppressNoImplicitAnyError) {
+                    else if (noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors && !(accessFlags & AccessFlags.SuppressNoImplicitAnyError)) {
                         if (propName !== undefined && typeHasStaticProperty(propName, objectType)) {
                             const typeName = typeToString(objectType);
                             error(accessExpression, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_to_access_the_static_member_2_instead, propName as string, typeName, typeName + "[" + getTextOfNode(accessExpression.argumentExpression) + "]");
@@ -14957,8 +14948,8 @@ namespace ts {
             return instantiateType(getTemplateTypeFromMappedType(objectType), templateMapper);
         }
 
-        function getIndexedAccessType(objectType: Type, indexType: Type, noUncheckedIndexedAccessCandidate?: boolean, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], accessFlags = AccessFlags.None): Type {
-            return getIndexedAccessTypeOrUndefined(objectType, indexType, noUncheckedIndexedAccessCandidate, accessNode, accessFlags, aliasSymbol, aliasTypeArguments) || (accessNode ? errorType : unknownType);
+        function getIndexedAccessType(objectType: Type, indexType: Type, accessFlags = AccessFlags.None, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type {
+            return getIndexedAccessTypeOrUndefined(objectType, indexType, accessFlags, accessNode, aliasSymbol, aliasTypeArguments) || (accessNode ? errorType : unknownType);
         }
 
         function indexTypeLessThan(indexType: Type, limit: number) {
@@ -14974,20 +14965,18 @@ namespace ts {
             });
         }
 
-        function getIndexedAccessTypeOrUndefined(objectType: Type, indexType: Type, noUncheckedIndexedAccessCandidate?: boolean, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, accessFlags = AccessFlags.None, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type | undefined {
+        function getIndexedAccessTypeOrUndefined(objectType: Type, indexType: Type, accessFlags = AccessFlags.None, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type | undefined {
             if (objectType === wildcardType || indexType === wildcardType) {
                 return wildcardType;
             }
-
-            const shouldIncludeUndefined = noUncheckedIndexedAccessCandidate ||
-                (!!compilerOptions.noUncheckedIndexedAccess &&
-                (accessFlags & (AccessFlags.Writing | AccessFlags.ExpressionPosition)) === AccessFlags.ExpressionPosition);
-
             // If the object type has a string index signature and no other members we know that the result will
             // always be the type of that index signature and we can simplify accordingly.
             if (isStringIndexSignatureOnlyType(objectType) && !(indexType.flags & TypeFlags.Nullable) && isTypeAssignableToKind(indexType, TypeFlags.String | TypeFlags.Number)) {
                 indexType = stringType;
             }
+            // In noUncheckedIndexedAccess mode, indexed access operations that occur in an expression in a read position and resolve to
+            // an index signature have 'undefined' included in their type.
+            if (compilerOptions.noUncheckedIndexedAccess && accessFlags & AccessFlags.ExpressionPosition) accessFlags |= AccessFlags.IncludeUndefined;
             // If the index type is generic, or if the object type is generic and doesn't originate in an expression and
             // the operation isn't exclusively indexing the fixed (non-variadic) portion of a tuple type, we are performing
             // a higher-order index access where we cannot meaningfully access the properties of the object type. Note that
@@ -15001,10 +14990,11 @@ namespace ts {
                     return objectType;
                 }
                 // Defer the operation by creating an indexed access type.
-                const id = objectType.id + "," + indexType.id + (shouldIncludeUndefined ? "?" : "") + getAliasId(aliasSymbol, aliasTypeArguments);
+                const persistentAccessFlags = accessFlags & AccessFlags.Persistent;
+                const id = objectType.id + "," + indexType.id + "," + persistentAccessFlags + getAliasId(aliasSymbol, aliasTypeArguments);
                 let type = indexedAccessTypes.get(id);
                 if (!type) {
-                    indexedAccessTypes.set(id, type = createIndexedAccessType(objectType, indexType, aliasSymbol, aliasTypeArguments, shouldIncludeUndefined));
+                    indexedAccessTypes.set(id, type = createIndexedAccessType(objectType, indexType, persistentAccessFlags, aliasSymbol, aliasTypeArguments));
                 }
 
                 return type;
@@ -15017,7 +15007,7 @@ namespace ts {
                 const propTypes: Type[] = [];
                 let wasMissingProp = false;
                 for (const t of (<UnionType>indexType).types) {
-                    const propType = getPropertyTypeForIndexType(objectType, apparentObjectType, t, indexType, wasMissingProp, accessNode, accessFlags, shouldIncludeUndefined);
+                    const propType = getPropertyTypeForIndexType(objectType, apparentObjectType, t, indexType, accessNode, accessFlags | (wasMissingProp ? AccessFlags.SuppressNoImplicitAnyError : 0));
                     if (propType) {
                         propTypes.push(propType);
                     }
@@ -15037,7 +15027,7 @@ namespace ts {
                     ? getIntersectionType(propTypes, aliasSymbol, aliasTypeArguments)
                     : getUnionType(propTypes, UnionReduction.Literal, aliasSymbol, aliasTypeArguments);
             }
-            return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, indexType, /* supressNoImplicitAnyError */ false, accessNode, accessFlags | AccessFlags.CacheSymbol, shouldIncludeUndefined, /* reportDeprecated */ true);
+            return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, indexType, accessNode, accessFlags | AccessFlags.CacheSymbol | AccessFlags.ReportDeprecated);
         }
 
         function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) {
@@ -15046,7 +15036,7 @@ namespace ts {
                 const objectType = getTypeFromTypeNode(node.objectType);
                 const indexType = getTypeFromTypeNode(node.indexType);
                 const potentialAlias = getAliasSymbolForTypeNode(node);
-                const resolved = getIndexedAccessType(objectType, indexType, /*noUncheckedIndexedAccessCandidate*/ undefined, node, potentialAlias, getTypeArgumentsForAliasSymbol(potentialAlias));
+                const resolved = getIndexedAccessType(objectType, indexType, AccessFlags.None, node, potentialAlias, getTypeArgumentsForAliasSymbol(potentialAlias));
                 links.resolvedType = resolved.flags & TypeFlags.IndexedAccess &&
                     (<IndexedAccessType>resolved).objectType === objectType &&
                     (<IndexedAccessType>resolved).indexType === indexType ?
@@ -16275,7 +16265,7 @@ namespace ts {
             if (flags & TypeFlags.IndexedAccess) {
                 const newAliasSymbol = aliasSymbol || type.aliasSymbol;
                 const newAliasTypeArguments = aliasSymbol ? aliasTypeArguments : instantiateTypes(type.aliasTypeArguments, mapper);
-                return getIndexedAccessType(instantiateType((<IndexedAccessType>type).objectType, mapper), instantiateType((<IndexedAccessType>type).indexType, mapper), (<IndexedAccessType>type).noUncheckedIndexedAccessCandidate, /*accessNode*/ undefined, newAliasSymbol, newAliasTypeArguments);
+                return getIndexedAccessType(instantiateType((<IndexedAccessType>type).objectType, mapper), instantiateType((<IndexedAccessType>type).indexType, mapper), (<IndexedAccessType>type).accessFlags, /*accessNode*/ undefined, newAliasSymbol, newAliasTypeArguments);
             }
             if (flags & TypeFlags.Conditional) {
                 return getConditionalTypeInstantiation(<ConditionalType>type, combineTypeMappers((<ConditionalType>type).mapper, mapper), aliasSymbol, aliasTypeArguments);
@@ -18466,7 +18456,7 @@ namespace ts {
                         const baseIndexType = getBaseConstraintOfType(indexType) || indexType;
                         if (!isGenericObjectType(baseObjectType) && !isGenericIndexType(baseIndexType)) {
                             const accessFlags = AccessFlags.Writing | (baseObjectType !== objectType ? AccessFlags.NoIndexSignatures : 0);
-                            const constraint = getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType, (<IndexedAccessType>target).noUncheckedIndexedAccessCandidate, /*accessNode*/ undefined, accessFlags);
+                            const constraint = getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType, accessFlags);
                             if (constraint) {
                                 if (reportErrors && originalErrorInfo) {
                                     // create a new chain for the constraint error
@@ -27943,8 +27933,8 @@ namespace ts {
             const effectiveIndexType = isForInVariableForNumericPropertyNames(indexExpression) ? numberType : indexType;
             const accessFlags = isAssignmentTarget(node) ?
                 AccessFlags.Writing | (isGenericObjectType(objectType) && !isThisTypeParameter(objectType) ? AccessFlags.NoIndexSignatures : 0) :
-                AccessFlags.None;
-            const indexedAccessType = getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, /*noUncheckedIndexedAccessCandidate*/ undefined, node, accessFlags | AccessFlags.ExpressionPosition) || errorType;
+                AccessFlags.ExpressionPosition;
+            const indexedAccessType = getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, accessFlags, node) || errorType;
             return checkIndexedAccessIndexType(getFlowTypeOfAccessExpression(node, getNodeLinks(node).resolvedSymbol, indexedAccessType, indexExpression, checkMode), node);
         }
 
@@ -31487,7 +31477,7 @@ namespace ts {
                         checkPropertyAccessibility(property, /*isSuper*/ false, /*writing*/ true, objectLiteralType, prop);
                     }
                 }
-                const elementType = getIndexedAccessType(objectLiteralType, exprType, /*noUncheckedIndexedAccessCandidate*/ undefined, name, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, AccessFlags.ExpressionPosition);
+                const elementType = getIndexedAccessType(objectLiteralType, exprType, AccessFlags.ExpressionPosition, name);
                 const type = getFlowTypeOfDestructuring(property, elementType);
                 return checkDestructuringAssignment(property.kind === SyntaxKind.ShorthandPropertyAssignment ? property : property.initializer, type);
             }
@@ -31548,7 +31538,7 @@ namespace ts {
                         // We create a synthetic expression so that getIndexedAccessType doesn't get confused
                         // when the element is a SyntaxKind.ElementAccessExpression.
                         const accessFlags = AccessFlags.ExpressionPosition | (hasDefaultValue(element) ? AccessFlags.NoTupleBoundsCheck : 0);
-                        const elementType = getIndexedAccessTypeOrUndefined(sourceType, indexType, /*noUncheckedIndexedAccessCandidate*/ undefined, createSyntheticExpression(element, indexType), accessFlags) || errorType;
+                        const elementType = getIndexedAccessTypeOrUndefined(sourceType, indexType, accessFlags, createSyntheticExpression(element, indexType)) || errorType;
                         const assignedType = hasDefaultValue(element) ? getTypeWithFacts(elementType, TypeFacts.NEUndefined) : elementType;
                         const type = getFlowTypeOfDestructuring(element, assignedType);
                         return checkDestructuringAssignment(element, type, checkMode);
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 430a2f2b20176..fd6d96d14296c 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -5491,17 +5491,27 @@ namespace ts {
         resolvedDefaultType?: Type;
     }
 
+    /* @internal */
+    export const enum AccessFlags {
+        None = 0,
+        IncludeUndefined = 1 << 0,
+        NoIndexSignatures = 1 << 1,
+        Writing = 1 << 2,
+        CacheSymbol = 1 << 3,
+        NoTupleBoundsCheck = 1 << 4,
+        ExpressionPosition = 1 << 5,
+        ReportDeprecated = 1 << 6,
+        SuppressNoImplicitAnyError = 1 << 7,
+        Persistent = IncludeUndefined,
+    }
+
     // Indexed access types (TypeFlags.IndexedAccess)
     // Possible forms are T[xxx], xxx[T], or xxx[keyof T], where T is a type variable
     export interface IndexedAccessType extends InstantiableType {
         objectType: Type;
         indexType: Type;
-        /**
-         * @internal
-         * Indicates that --noUncheckedIndexedAccess may introduce 'undefined' into
-         * the resulting type, depending on how type variable constraints are resolved.
-         */
-        noUncheckedIndexedAccessCandidate: boolean;
+        /* @internal */
+        accessFlags: AccessFlags;  // Only includes AccessFlags.Persistent
         constraint?: Type;
         simplifiedForReading?: Type;
         simplifiedForWriting?: Type;

From e8899c1527a8c4fdac8533a4edd590b3e7f27316 Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Mon, 17 May 2021 08:26:44 -0700
Subject: [PATCH 2/3] Preserve generics in contextual types for rest arguments

---
 src/compiler/checker.ts | 10 ++++++++--
 src/compiler/types.ts   |  1 +
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 60f8a0f6fa881..c89ae60c11f78 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -14609,6 +14609,9 @@ namespace ts {
             const propName = accessNode && isPrivateIdentifier(accessNode) ? undefined : getPropertyNameFromIndex(indexType, accessNode);
 
             if (propName !== undefined) {
+                if (accessFlags & AccessFlags.Contextual) {
+                    return getTypeOfPropertyOfContextualType(objectType, propName) || anyType;
+                }
                 const prop = getPropertyOfType(objectType, propName);
                 if (prop) {
                     if (accessFlags & AccessFlags.ReportDeprecated && accessNode && prop.declarations && getDeclarationNodeFlagsFromSymbol(prop) & NodeFlags.Deprecated && isUncalledFunctionReference(accessNode, prop)) {
@@ -25176,7 +25179,10 @@ namespace ts {
             if (isJsxOpeningLikeElement(callTarget) && argIndex === 0) {
                 return getEffectiveFirstArgumentForJsxSignature(signature, callTarget);
             }
-            return getTypeAtPosition(signature, argIndex);
+            const restIndex = signature.parameters.length - 1;
+            return signatureHasRestParameter(signature) && argIndex >= restIndex ?
+                getIndexedAccessType(getTypeOfSymbol(signature.parameters[restIndex]), getLiteralType(argIndex - restIndex), AccessFlags.Contextual) :
+                getTypeAtPosition(signature, argIndex);
         }
 
         function getContextualTypeForSubstitutionExpression(template: TemplateExpression, substitutionExpression: Expression) {
@@ -28291,7 +28297,7 @@ namespace ts {
                     }
                 }
                 else {
-                    const contextualType = getIndexedAccessType(restType, getLiteralType(i - index));
+                    const contextualType = getIndexedAccessType(restType, getLiteralType(i - index), AccessFlags.Contextual);
                     const argType = checkExpressionWithContextualType(arg, contextualType, context, checkMode);
                     const hasPrimitiveContextualType = maybeTypeOfKind(contextualType, TypeFlags.Primitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping);
                     types.push(hasPrimitiveContextualType ? getRegularTypeOfLiteralType(argType) : getWidenedLiteralType(argType));
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index fd6d96d14296c..7b7dee85a0c24 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -5502,6 +5502,7 @@ namespace ts {
         ExpressionPosition = 1 << 5,
         ReportDeprecated = 1 << 6,
         SuppressNoImplicitAnyError = 1 << 7,
+        Contextual = 1 << 8,
         Persistent = IncludeUndefined,
     }
 

From 84e7ad5ebb5cd66de5a943c69f9a508d736fff7f Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Mon, 17 May 2021 08:26:54 -0700
Subject: [PATCH 3/3] Add regression test

---
 .../controlFlowGenericTypes.errors.txt        | 10 +++++
 .../reference/controlFlowGenericTypes.js      | 26 +++++++++++++
 .../reference/controlFlowGenericTypes.symbols | 38 +++++++++++++++++++
 .../reference/controlFlowGenericTypes.types   | 33 ++++++++++++++++
 .../controlFlow/controlFlowGenericTypes.ts    | 10 +++++
 5 files changed, 117 insertions(+)

diff --git a/tests/baselines/reference/controlFlowGenericTypes.errors.txt b/tests/baselines/reference/controlFlowGenericTypes.errors.txt
index 09eb8c96198cb..36d9e896317e2 100644
--- a/tests/baselines/reference/controlFlowGenericTypes.errors.txt
+++ b/tests/baselines/reference/controlFlowGenericTypes.errors.txt
@@ -149,4 +149,14 @@ tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(91,11): error TS2
         }
         return 0;
     };
+    
+    // Repro from #44093
+    
+    class EventEmitter<ET> {
+        off<K extends keyof ET>(...args: [K, number] | [unknown, string]):void {}
+    }
+    function once<ET, T extends EventEmitter<ET>>(emittingObject: T, eventName: keyof ET): void {
+        emittingObject.off(eventName, 0);
+        emittingObject.off(eventName as typeof eventName, 0);
+    }
     
\ No newline at end of file
diff --git a/tests/baselines/reference/controlFlowGenericTypes.js b/tests/baselines/reference/controlFlowGenericTypes.js
index 8372b15dae476..17b8e0dd4e087 100644
--- a/tests/baselines/reference/controlFlowGenericTypes.js
+++ b/tests/baselines/reference/controlFlowGenericTypes.js
@@ -128,6 +128,16 @@ function get<K extends keyof A>(key: K, obj: A): number {
     }
     return 0;
 };
+
+// Repro from #44093
+
+class EventEmitter<ET> {
+    off<K extends keyof ET>(...args: [K, number] | [unknown, string]):void {}
+}
+function once<ET, T extends EventEmitter<ET>>(emittingObject: T, eventName: keyof ET): void {
+    emittingObject.off(eventName, 0);
+    emittingObject.off(eventName as typeof eventName, 0);
+}
 
 
 //// [controlFlowGenericTypes.js]
@@ -220,3 +230,19 @@ function get(key, obj) {
     return 0;
 }
 ;
+// Repro from #44093
+var EventEmitter = /** @class */ (function () {
+    function EventEmitter() {
+    }
+    EventEmitter.prototype.off = function () {
+        var args = [];
+        for (var _i = 0; _i < arguments.length; _i++) {
+            args[_i] = arguments[_i];
+        }
+    };
+    return EventEmitter;
+}());
+function once(emittingObject, eventName) {
+    emittingObject.off(eventName, 0);
+    emittingObject.off(eventName, 0);
+}
diff --git a/tests/baselines/reference/controlFlowGenericTypes.symbols b/tests/baselines/reference/controlFlowGenericTypes.symbols
index 8c689bd468ac5..61597dfdf9d91 100644
--- a/tests/baselines/reference/controlFlowGenericTypes.symbols
+++ b/tests/baselines/reference/controlFlowGenericTypes.symbols
@@ -367,3 +367,41 @@ function get<K extends keyof A>(key: K, obj: A): number {
     return 0;
 };
 
+// Repro from #44093
+
+class EventEmitter<ET> {
+>EventEmitter : Symbol(EventEmitter, Decl(controlFlowGenericTypes.ts, 128, 2))
+>ET : Symbol(ET, Decl(controlFlowGenericTypes.ts, 132, 19))
+
+    off<K extends keyof ET>(...args: [K, number] | [unknown, string]):void {}
+>off : Symbol(EventEmitter.off, Decl(controlFlowGenericTypes.ts, 132, 24))
+>K : Symbol(K, Decl(controlFlowGenericTypes.ts, 133, 8))
+>ET : Symbol(ET, Decl(controlFlowGenericTypes.ts, 132, 19))
+>args : Symbol(args, Decl(controlFlowGenericTypes.ts, 133, 28))
+>K : Symbol(K, Decl(controlFlowGenericTypes.ts, 133, 8))
+}
+function once<ET, T extends EventEmitter<ET>>(emittingObject: T, eventName: keyof ET): void {
+>once : Symbol(once, Decl(controlFlowGenericTypes.ts, 134, 1))
+>ET : Symbol(ET, Decl(controlFlowGenericTypes.ts, 135, 14))
+>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 135, 17))
+>EventEmitter : Symbol(EventEmitter, Decl(controlFlowGenericTypes.ts, 128, 2))
+>ET : Symbol(ET, Decl(controlFlowGenericTypes.ts, 135, 14))
+>emittingObject : Symbol(emittingObject, Decl(controlFlowGenericTypes.ts, 135, 46))
+>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 135, 17))
+>eventName : Symbol(eventName, Decl(controlFlowGenericTypes.ts, 135, 64))
+>ET : Symbol(ET, Decl(controlFlowGenericTypes.ts, 135, 14))
+
+    emittingObject.off(eventName, 0);
+>emittingObject.off : Symbol(EventEmitter.off, Decl(controlFlowGenericTypes.ts, 132, 24))
+>emittingObject : Symbol(emittingObject, Decl(controlFlowGenericTypes.ts, 135, 46))
+>off : Symbol(EventEmitter.off, Decl(controlFlowGenericTypes.ts, 132, 24))
+>eventName : Symbol(eventName, Decl(controlFlowGenericTypes.ts, 135, 64))
+
+    emittingObject.off(eventName as typeof eventName, 0);
+>emittingObject.off : Symbol(EventEmitter.off, Decl(controlFlowGenericTypes.ts, 132, 24))
+>emittingObject : Symbol(emittingObject, Decl(controlFlowGenericTypes.ts, 135, 46))
+>off : Symbol(EventEmitter.off, Decl(controlFlowGenericTypes.ts, 132, 24))
+>eventName : Symbol(eventName, Decl(controlFlowGenericTypes.ts, 135, 64))
+>eventName : Symbol(eventName, Decl(controlFlowGenericTypes.ts, 135, 64))
+}
+
diff --git a/tests/baselines/reference/controlFlowGenericTypes.types b/tests/baselines/reference/controlFlowGenericTypes.types
index 66f895f245d34..8c62c6dfa128a 100644
--- a/tests/baselines/reference/controlFlowGenericTypes.types
+++ b/tests/baselines/reference/controlFlowGenericTypes.types
@@ -355,3 +355,36 @@ function get<K extends keyof A>(key: K, obj: A): number {
 
 };
 
+// Repro from #44093
+
+class EventEmitter<ET> {
+>EventEmitter : EventEmitter<ET>
+
+    off<K extends keyof ET>(...args: [K, number] | [unknown, string]):void {}
+>off : <K extends keyof ET>(...args: [K, number] | [unknown, string]) => void
+>args : [K, number] | [unknown, string]
+}
+function once<ET, T extends EventEmitter<ET>>(emittingObject: T, eventName: keyof ET): void {
+>once : <ET, T extends EventEmitter<ET>>(emittingObject: T, eventName: keyof ET) => void
+>emittingObject : T
+>eventName : keyof ET
+
+    emittingObject.off(eventName, 0);
+>emittingObject.off(eventName, 0) : void
+>emittingObject.off : <K extends keyof ET>(...args: [unknown, string] | [K, number]) => void
+>emittingObject : T
+>off : <K extends keyof ET>(...args: [unknown, string] | [K, number]) => void
+>eventName : keyof ET
+>0 : 0
+
+    emittingObject.off(eventName as typeof eventName, 0);
+>emittingObject.off(eventName as typeof eventName, 0) : void
+>emittingObject.off : <K extends keyof ET>(...args: [unknown, string] | [K, number]) => void
+>emittingObject : T
+>off : <K extends keyof ET>(...args: [unknown, string] | [K, number]) => void
+>eventName as typeof eventName : keyof ET
+>eventName : keyof ET
+>eventName : keyof ET
+>0 : 0
+}
+
diff --git a/tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts b/tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts
index 29ea2d6814a1b..21dbde8c33cb9 100644
--- a/tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts
+++ b/tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts
@@ -129,3 +129,13 @@ function get<K extends keyof A>(key: K, obj: A): number {
     }
     return 0;
 };
+
+// Repro from #44093
+
+class EventEmitter<ET> {
+    off<K extends keyof ET>(...args: [K, number] | [unknown, string]):void {}
+}
+function once<ET, T extends EventEmitter<ET>>(emittingObject: T, eventName: keyof ET): void {
+    emittingObject.off(eventName, 0);
+    emittingObject.off(eventName as typeof eventName, 0);
+}