@@ -22694,11 +22694,12 @@ namespace ts {
2269422694 return false;
2269522695 }
2269622696
22697- function getAccessedPropertyName(access: AccessExpression | BindingElement): __String | undefined {
22697+ function getAccessedPropertyName(access: AccessExpression | BindingElement | ParameterDeclaration ): __String | undefined {
2269822698 let propertyName;
2269922699 return access.kind === SyntaxKind.PropertyAccessExpression ? access.name.escapedText :
2270022700 access.kind === SyntaxKind.ElementAccessExpression && isStringOrNumericLiteralLike(access.argumentExpression) ? escapeLeadingUnderscores(access.argumentExpression.text) :
2270122701 access.kind === SyntaxKind.BindingElement && (propertyName = getDestructuringPropertyName(access)) ? escapeLeadingUnderscores(propertyName) :
22702+ access.kind === SyntaxKind.Parameter ? ("" + access.parent.parameters.indexOf(access)) as __String :
2270222703 undefined;
2270322704 }
2270422705
@@ -24120,13 +24121,14 @@ namespace ts {
2412024121 }
2412124122
2412224123 function getCandidateDiscriminantPropertyAccess(expr: Expression) {
24123- if (isBindingPattern(reference)) {
24124- // When the reference is a binding pattern, we are narrowing a pesudo-reference in getNarrowedTypeOfSymbol.
24125- // An identifier for a destructuring variable declared in the same binding pattern is a candidate.
24124+ if (isBindingPattern(reference) || isFunctionExpressionOrArrowFunction(reference)) {
24125+ // When the reference is a binding pattern or function or arrow expression, we are narrowing a pesudo-reference in
24126+ // getNarrowedTypeOfSymbol. An identifier for a destructuring variable declared in the same binding pattern or
24127+ // parameter declared in the same parameter list is a candidate.
2412624128 if (isIdentifier(expr)) {
2412724129 const symbol = getResolvedSymbol(expr);
2412824130 const declaration = symbol.valueDeclaration;
24129- if (declaration && isBindingElement(declaration) && ! declaration.initializer && !declaration.dotDotDotToken && reference === declaration.parent ) {
24131+ if (declaration && ( isBindingElement(declaration) || isParameter(declaration)) && reference === declaration.parent && !declaration.initializer && ! declaration.dotDotDotToken ) {
2413024132 return declaration;
2413124133 }
2413224134 }
@@ -24173,7 +24175,7 @@ namespace ts {
2417324175 return undefined;
2417424176 }
2417524177
24176- function narrowTypeByDiscriminant(type: Type, access: AccessExpression | BindingElement, narrowType: (t: Type) => Type): Type {
24178+ function narrowTypeByDiscriminant(type: Type, access: AccessExpression | BindingElement | ParameterDeclaration , narrowType: (t: Type) => Type): Type {
2417724179 const propName = getAccessedPropertyName(access);
2417824180 if (propName === undefined) {
2417924181 return type;
@@ -24191,7 +24193,7 @@ namespace ts {
2419124193 });
2419224194 }
2419324195
24194- function narrowTypeByDiscriminantProperty(type: Type, access: AccessExpression | BindingElement, operator: SyntaxKind, value: Expression, assumeTrue: boolean) {
24196+ function narrowTypeByDiscriminantProperty(type: Type, access: AccessExpression | BindingElement | ParameterDeclaration , operator: SyntaxKind, value: Expression, assumeTrue: boolean) {
2419524197 if ((operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) && type.flags & TypeFlags.Union) {
2419624198 const keyPropertyName = getKeyPropertyName(type as UnionType);
2419724199 if (keyPropertyName && keyPropertyName === getAccessedPropertyName(access)) {
@@ -24206,7 +24208,7 @@ namespace ts {
2420624208 return narrowTypeByDiscriminant(type, access, t => narrowTypeByEquality(t, operator, value, assumeTrue));
2420724209 }
2420824210
24209- function narrowTypeBySwitchOnDiscriminantProperty(type: Type, access: AccessExpression | BindingElement, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) {
24211+ function narrowTypeBySwitchOnDiscriminantProperty(type: Type, access: AccessExpression | BindingElement | ParameterDeclaration , switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) {
2421024212 if (clauseStart < clauseEnd && type.flags & TypeFlags.Union && getKeyPropertyName(type as UnionType) === getAccessedPropertyName(access)) {
2421124213 const clauseTypes = getSwitchClauseTypes(switchStatement).slice(clauseStart, clauseEnd);
2421224214 const candidate = getUnionType(map(clauseTypes, t => getConstituentTypeForKeyType(type as UnionType, t) || unknownType));
@@ -24984,42 +24986,78 @@ namespace ts {
2498424986 }
2498524987
2498624988 function getNarrowedTypeOfSymbol(symbol: Symbol, location: Identifier) {
24987- // If we have a non-rest binding element with no initializer declared as a const variable or a const-like
24988- // parameter (a parameter for which there are no assignments in the function body), and if the parent type
24989- // for the destructuring is a union type, one or more of the binding elements may represent discriminant
24990- // properties, and we want the effects of conditional checks on such discriminants to affect the types of
24991- // other binding elements from the same destructuring. Consider:
24992- //
24993- // type Action =
24994- // | { kind: 'A', payload: number }
24995- // | { kind: 'B', payload: string };
24996- //
24997- // function f1({ kind, payload }: Action) {
24998- // if (kind === 'A') {
24999- // payload.toFixed();
25000- // }
25001- // if (kind === 'B') {
25002- // payload.toUpperCase();
25003- // }
25004- // }
25005- //
25006- // Above, we want the conditional checks on 'kind' to affect the type of 'payload'. To facilitate this, we use
25007- // the binding pattern AST instance for '{ kind, payload }' as a pseudo-reference and narrow this reference
25008- // as if it occurred in the specified location. We then recompute the narrowed binding element type by
25009- // destructuring from the narrowed parent type.
2501024989 const declaration = symbol.valueDeclaration;
25011- if (declaration && isBindingElement(declaration) && !declaration.initializer && !declaration.dotDotDotToken && declaration.parent.elements.length >= 2) {
25012- const parent = declaration.parent.parent;
25013- if (parent.kind === SyntaxKind.VariableDeclaration && getCombinedNodeFlags(declaration) & NodeFlags.Const || parent.kind === SyntaxKind.Parameter) {
25014- const links = getNodeLinks(location);
25015- if (!(links.flags & NodeCheckFlags.InCheckIdentifier)) {
25016- links.flags |= NodeCheckFlags.InCheckIdentifier;
25017- const parentType = getTypeForBindingElementParent(parent);
25018- links.flags &= ~NodeCheckFlags.InCheckIdentifier;
25019- if (parentType && parentType.flags & TypeFlags.Union && !(parent.kind === SyntaxKind.Parameter && isSymbolAssigned(symbol))) {
25020- const pattern = declaration.parent;
25021- const narrowedType = getFlowTypeOfReference(pattern, parentType, parentType, /*flowContainer*/ undefined, location.flowNode);
25022- return getBindingElementTypeFromParentType(declaration, narrowedType);
24990+ if (declaration) {
24991+ // If we have a non-rest binding element with no initializer declared as a const variable or a const-like
24992+ // parameter (a parameter for which there are no assignments in the function body), and if the parent type
24993+ // for the destructuring is a union type, one or more of the binding elements may represent discriminant
24994+ // properties, and we want the effects of conditional checks on such discriminants to affect the types of
24995+ // other binding elements from the same destructuring. Consider:
24996+ //
24997+ // type Action =
24998+ // | { kind: 'A', payload: number }
24999+ // | { kind: 'B', payload: string };
25000+ //
25001+ // function f({ kind, payload }: Action) {
25002+ // if (kind === 'A') {
25003+ // payload.toFixed();
25004+ // }
25005+ // if (kind === 'B') {
25006+ // payload.toUpperCase();
25007+ // }
25008+ // }
25009+ //
25010+ // Above, we want the conditional checks on 'kind' to affect the type of 'payload'. To facilitate this, we use
25011+ // the binding pattern AST instance for '{ kind, payload }' as a pseudo-reference and narrow this reference
25012+ // as if it occurred in the specified location. We then recompute the narrowed binding element type by
25013+ // destructuring from the narrowed parent type.
25014+ if (isBindingElement(declaration) && !declaration.initializer && !declaration.dotDotDotToken && declaration.parent.elements.length >= 2) {
25015+ const parent = declaration.parent.parent;
25016+ if (parent.kind === SyntaxKind.VariableDeclaration && getCombinedNodeFlags(declaration) & NodeFlags.Const || parent.kind === SyntaxKind.Parameter) {
25017+ const links = getNodeLinks(location);
25018+ if (!(links.flags & NodeCheckFlags.InCheckIdentifier)) {
25019+ links.flags |= NodeCheckFlags.InCheckIdentifier;
25020+ const parentType = getTypeForBindingElementParent(parent);
25021+ links.flags &= ~NodeCheckFlags.InCheckIdentifier;
25022+ if (parentType && parentType.flags & TypeFlags.Union && !(parent.kind === SyntaxKind.Parameter && isSymbolAssigned(symbol))) {
25023+ const pattern = declaration.parent;
25024+ const narrowedType = getFlowTypeOfReference(pattern, parentType, parentType, /*flowContainer*/ undefined, location.flowNode);
25025+ return getBindingElementTypeFromParentType(declaration, narrowedType);
25026+ }
25027+ }
25028+ }
25029+ }
25030+ // If we have a const-like parameter with no type annotation or initializer, and if the parameter is contextually
25031+ // typed by a signature with a single rest parameter of a union of tuple types, one or more of the parameters may
25032+ // represent discriminant tuple elements, and we want the effects of conditional checks on such discriminants to
25033+ // affect the types of other parameters in the same parameter list. Consider:
25034+ //
25035+ // type Action = [kind: 'A', payload: number] | [kind: 'B', payload: string];
25036+ //
25037+ // const f: (...args: Action) => void = (kind, payload) => {
25038+ // if (kind === 'A') {
25039+ // payload.toFixed();
25040+ // }
25041+ // if (kind === 'B') {
25042+ // payload.toUpperCase();
25043+ // }
25044+ // }
25045+ //
25046+ // Above, we want the conditional checks on 'kind' to affect the type of 'payload'. To facilitate this, we use
25047+ // the arrow function AST node for '(kind, payload) => ...' as a pseudo-reference and narrow this reference as
25048+ // if it occurred in the specified location. We then recompute the narrowed parameter type by indexing into the
25049+ // narrowed tuple type.
25050+ if (isParameter(declaration) && !declaration.type && !declaration.initializer && !declaration.dotDotDotToken) {
25051+ const func = declaration.parent;
25052+ if (func.parameters.length >= 2 && isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
25053+ const contextualSignature = getContextualSignature(func);
25054+ if (contextualSignature && contextualSignature.parameters.length === 1 && signatureHasRestParameter(contextualSignature)) {
25055+ const restType = getTypeOfSymbol(contextualSignature.parameters[0]);
25056+ if (restType.flags & TypeFlags.Union && everyType(restType, isTupleType) && !isSymbolAssigned(symbol)) {
25057+ const narrowedType = getFlowTypeOfReference(func, restType, restType, /*flowContainer*/ undefined, location.flowNode);
25058+ const index = func.parameters.indexOf(declaration) - (getThisParameter(func) ? 1 : 0);
25059+ return getIndexedAccessType(narrowedType, getNumberLiteralType(index));
25060+ }
2502325061 }
2502425062 }
2502525063 }
0 commit comments