@@ -22694,11 +22694,12 @@ namespace ts {
22694
22694
return false;
22695
22695
}
22696
22696
22697
- function getAccessedPropertyName(access: AccessExpression | BindingElement): __String | undefined {
22697
+ function getAccessedPropertyName(access: AccessExpression | BindingElement | ParameterDeclaration ): __String | undefined {
22698
22698
let propertyName;
22699
22699
return access.kind === SyntaxKind.PropertyAccessExpression ? access.name.escapedText :
22700
22700
access.kind === SyntaxKind.ElementAccessExpression && isStringOrNumericLiteralLike(access.argumentExpression) ? escapeLeadingUnderscores(access.argumentExpression.text) :
22701
22701
access.kind === SyntaxKind.BindingElement && (propertyName = getDestructuringPropertyName(access)) ? escapeLeadingUnderscores(propertyName) :
22702
+ access.kind === SyntaxKind.Parameter ? ("" + access.parent.parameters.indexOf(access)) as __String :
22702
22703
undefined;
22703
22704
}
22704
22705
@@ -24120,13 +24121,14 @@ namespace ts {
24120
24121
}
24121
24122
24122
24123
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.
24126
24128
if (isIdentifier(expr)) {
24127
24129
const symbol = getResolvedSymbol(expr);
24128
24130
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 ) {
24130
24132
return declaration;
24131
24133
}
24132
24134
}
@@ -24173,7 +24175,7 @@ namespace ts {
24173
24175
return undefined;
24174
24176
}
24175
24177
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 {
24177
24179
const propName = getAccessedPropertyName(access);
24178
24180
if (propName === undefined) {
24179
24181
return type;
@@ -24191,7 +24193,7 @@ namespace ts {
24191
24193
});
24192
24194
}
24193
24195
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) {
24195
24197
if ((operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) && type.flags & TypeFlags.Union) {
24196
24198
const keyPropertyName = getKeyPropertyName(type as UnionType);
24197
24199
if (keyPropertyName && keyPropertyName === getAccessedPropertyName(access)) {
@@ -24206,7 +24208,7 @@ namespace ts {
24206
24208
return narrowTypeByDiscriminant(type, access, t => narrowTypeByEquality(t, operator, value, assumeTrue));
24207
24209
}
24208
24210
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) {
24210
24212
if (clauseStart < clauseEnd && type.flags & TypeFlags.Union && getKeyPropertyName(type as UnionType) === getAccessedPropertyName(access)) {
24211
24213
const clauseTypes = getSwitchClauseTypes(switchStatement).slice(clauseStart, clauseEnd);
24212
24214
const candidate = getUnionType(map(clauseTypes, t => getConstituentTypeForKeyType(type as UnionType, t) || unknownType));
@@ -24984,42 +24986,78 @@ namespace ts {
24984
24986
}
24985
24987
24986
24988
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.
25010
24989
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
+ }
25023
25061
}
25024
25062
}
25025
25063
}
0 commit comments