From c13b93670b0ff5cac2adc39db0201f70b02d2f7b Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 26 Aug 2016 10:04:49 -0700 Subject: [PATCH 1/6] Narrow on element access of known property Narrowing now happens for x['knownProperty'] as well as x.knownProperty --- src/compiler/binder.ts | 19 ++++++++++++++++--- src/compiler/checker.ts | 29 +++++++++++++++++++---------- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d8017d601adb0..e63519c953bf0 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -594,6 +594,7 @@ namespace ts { case SyntaxKind.Identifier: case SyntaxKind.ThisKeyword: case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: return isNarrowableReference(expr); case SyntaxKind.CallExpression: return hasNarrowableArgument(expr); @@ -608,9 +609,21 @@ namespace ts { } function isNarrowableReference(expr: Expression): boolean { - return expr.kind === SyntaxKind.Identifier || - expr.kind === SyntaxKind.ThisKeyword || - expr.kind === SyntaxKind.PropertyAccessExpression && isNarrowableReference((expr).expression); + if (expr.kind === SyntaxKind.ElementAccessExpression) { + const argument = (expr as ElementAccessExpression).argumentExpression; + return argument.kind === SyntaxKind.StringLiteral || argument.kind === SyntaxKind.NumericLiteral || argument.kind === SyntaxKind.EnumMember; + } + while (true) { + if (expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword) { + return true; + } + else if (expr.kind === SyntaxKind.PropertyAccessExpression) { + expr = (expr as PropertyAccessExpression).expression; + } + else { + return false; + } + } } function hasNarrowableArgument(expr: CallExpression) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2341a0f7dfd09..6791f5bf35074 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8347,7 +8347,9 @@ namespace ts { type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); } else if (isMatchingReferenceDiscriminant(expr)) { - type = narrowTypeByDiscriminant(type, expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd)); + type = narrowTypeByDiscriminant(type, + expr as PropertyAccessExpression | ElementAccessExpression, + t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd)); } return createFlowType(type, isIncomplete(flowType)); } @@ -8450,14 +8452,21 @@ namespace ts { } function isMatchingReferenceDiscriminant(expr: Expression) { - return expr.kind === SyntaxKind.PropertyAccessExpression && - declaredType.flags & TypeFlags.Union && - isMatchingReference(reference, (expr).expression) && - isDiscriminantProperty(declaredType, (expr).name.text); + if (!(declaredType.flags & TypeFlags.Union) || + expr.kind !== SyntaxKind.PropertyAccessExpression && expr.kind !== SyntaxKind.ElementAccessExpression) { + return false; + } + const name = expr.kind === SyntaxKind.PropertyAccessExpression ? + (expr as PropertyAccessExpression).name.text : + getPropertyNameForIndexedAccess((expr as ElementAccessExpression).argumentExpression, declaredType); + return isMatchingReference(reference, (expr as PropertyAccessExpression | ElementAccessExpression).expression) && + isDiscriminantProperty(declaredType, name); } - function narrowTypeByDiscriminant(type: Type, propAccess: PropertyAccessExpression, narrowType: (t: Type) => Type): Type { - const propName = propAccess.name.text; + function narrowTypeByDiscriminant(type: Type, access: PropertyAccessExpression | ElementAccessExpression, narrowType: (t: Type) => Type): Type { + const propName = access.kind === SyntaxKind.PropertyAccessExpression ? + (access as PropertyAccessExpression).name.text : + getPropertyNameForIndexedAccess((access as ElementAccessExpression).argumentExpression, type); const propType = getTypeOfPropertyOfType(type, propName); const narrowedPropType = propType && narrowType(propType); return propType === narrowedPropType ? type : filterType(type, t => isTypeComparableTo(getTypeOfPropertyOfType(t, propName), narrowedPropType)); @@ -8468,7 +8477,7 @@ namespace ts { return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy); } if (isMatchingReferenceDiscriminant(expr)) { - return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy)); + return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy)); } if (containsMatchingReferenceDiscriminant(reference, expr)) { return declaredType; @@ -8500,10 +8509,10 @@ namespace ts { return narrowTypeByEquality(type, operator, left, assumeTrue); } if (isMatchingReferenceDiscriminant(left)) { - return narrowTypeByDiscriminant(type, left, t => narrowTypeByEquality(t, operator, right, assumeTrue)); + return narrowTypeByDiscriminant(type, left, t => narrowTypeByEquality(t, operator, right, assumeTrue)); } if (isMatchingReferenceDiscriminant(right)) { - return narrowTypeByDiscriminant(type, right, t => narrowTypeByEquality(t, operator, left, assumeTrue)); + return narrowTypeByDiscriminant(type, right, t => narrowTypeByEquality(t, operator, left, assumeTrue)); } if (containsMatchingReferenceDiscriminant(reference, left) || containsMatchingReferenceDiscriminant(reference, right)) { return declaredType; From 3ca3d30a83b84a15ccc47de5a05a0b8ae1d703ec Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 26 Aug 2016 10:05:48 -0700 Subject: [PATCH 2/6] Test narrowing of element access of known property --- ...uardNarrowsIndexedAccessOfKnownProperty.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts diff --git a/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts b/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts new file mode 100644 index 0000000000000..94bb29ac9aef6 --- /dev/null +++ b/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts @@ -0,0 +1,28 @@ +interface Square { + kind: "square"; + size: number; +} + +interface Rectangle { + kind: "rectangle"; + width: number; + height: number; +} + +interface Circle { + kind: "circle"; + radius: number; +} + +type Shape = Square | Rectangle | Circle; + +function area(s: Shape) { + // In the following switch statement, the type of s is narrowed in each case clause + // according to the value of the discriminant property, thus allowing the other properties + // of that variant to be accessed without a type assertion. + switch (s['kind']) { + case "square": return s.size * s.size; + case "rectangle": return s.width * s.height; + case "circle": return Math.PI * s.radius * s.radius; + } +} From d05e354a7092cc1d8c8d14f68d3cebe501d826eb Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 26 Aug 2016 10:07:50 -0700 Subject: [PATCH 3/6] Add baselines, which I missed --- ...uardNarrowsIndexedAccessOfKnownProperty.js | 42 +++++++++ ...arrowsIndexedAccessOfKnownProperty.symbols | 81 +++++++++++++++++ ...dNarrowsIndexedAccessOfKnownProperty.types | 89 +++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js create mode 100644 tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.symbols create mode 100644 tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types diff --git a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js new file mode 100644 index 0000000000000..fd88e201c969a --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js @@ -0,0 +1,42 @@ +//// [typeGuardNarrowsIndexedAccessOfKnownProperty.ts] +interface Square { + kind: "square"; + size: number; +} + +interface Rectangle { + kind: "rectangle"; + width: number; + height: number; +} + +interface Circle { + kind: "circle"; + radius: number; +} + +type Shape = Square | Rectangle | Circle; + +function area(s: Shape) { + // In the following switch statement, the type of s is narrowed in each case clause + // according to the value of the discriminant property, thus allowing the other properties + // of that variant to be accessed without a type assertion. + switch (s['kind']) { + case "square": return s.size * s.size; + case "rectangle": return s.width * s.height; + case "circle": return Math.PI * s.radius * s.radius; + } +} + + +//// [typeGuardNarrowsIndexedAccessOfKnownProperty.js] +function area(s) { + // In the following switch statement, the type of s is narrowed in each case clause + // according to the value of the discriminant property, thus allowing the other properties + // of that variant to be accessed without a type assertion. + switch (s['kind']) { + case "square": return s.size * s.size; + case "rectangle": return s.width * s.height; + case "circle": return Math.PI * s.radius * s.radius; + } +} diff --git a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.symbols b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.symbols new file mode 100644 index 0000000000000..dd78d1ed062cc --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.symbols @@ -0,0 +1,81 @@ +=== tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts === +interface Square { +>Square : Symbol(Square, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 0)) + + kind: "square"; +>kind : Symbol(Square.kind, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 18)) + + size: number; +>size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) +} + +interface Rectangle { +>Rectangle : Symbol(Rectangle, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 3, 1)) + + kind: "rectangle"; +>kind : Symbol(Rectangle.kind, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 5, 21)) + + width: number; +>width : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 6, 22)) + + height: number; +>height : Symbol(Rectangle.height, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 7, 18)) +} + +interface Circle { +>Circle : Symbol(Circle, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 9, 1)) + + kind: "circle"; +>kind : Symbol(Circle.kind, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 11, 18)) + + radius: number; +>radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 19)) +} + +type Shape = Square | Rectangle | Circle; +>Shape : Symbol(Shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 14, 1)) +>Square : Symbol(Square, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 0)) +>Rectangle : Symbol(Rectangle, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 3, 1)) +>Circle : Symbol(Circle, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 9, 1)) + +function area(s: Shape) { +>area : Symbol(area, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 41)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) +>Shape : Symbol(Shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 14, 1)) + + // In the following switch statement, the type of s is narrowed in each case clause + // according to the value of the discriminant property, thus allowing the other properties + // of that variant to be accessed without a type assertion. + switch (s['kind']) { +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) +>'kind' : Symbol(kind, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 18), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 5, 21), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 11, 18)) + + case "square": return s.size * s.size; +>s.size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) +>size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) +>s.size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) +>size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) + + case "rectangle": return s.width * s.height; +>s.width : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 6, 22)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) +>width : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 6, 22)) +>s.height : Symbol(Rectangle.height, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 7, 18)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) +>height : Symbol(Rectangle.height, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 7, 18)) + + case "circle": return Math.PI * s.radius * s.radius; +>Math.PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>s.radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 19)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) +>radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 19)) +>s.radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 19)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) +>radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 19)) + } +} + diff --git a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types new file mode 100644 index 0000000000000..794c767afe7ae --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types @@ -0,0 +1,89 @@ +=== tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts === +interface Square { +>Square : Square + + kind: "square"; +>kind : "square" + + size: number; +>size : number +} + +interface Rectangle { +>Rectangle : Rectangle + + kind: "rectangle"; +>kind : "rectangle" + + width: number; +>width : number + + height: number; +>height : number +} + +interface Circle { +>Circle : Circle + + kind: "circle"; +>kind : "circle" + + radius: number; +>radius : number +} + +type Shape = Square | Rectangle | Circle; +>Shape : Shape +>Square : Square +>Rectangle : Rectangle +>Circle : Circle + +function area(s: Shape) { +>area : (s: Shape) => number +>s : Shape +>Shape : Shape + + // In the following switch statement, the type of s is narrowed in each case clause + // according to the value of the discriminant property, thus allowing the other properties + // of that variant to be accessed without a type assertion. + switch (s['kind']) { +>s['kind'] : "square" | "rectangle" | "circle" +>s : Shape +>'kind' : string + + case "square": return s.size * s.size; +>"square" : "square" +>s.size * s.size : number +>s.size : number +>s : Square +>size : number +>s.size : number +>s : Square +>size : number + + case "rectangle": return s.width * s.height; +>"rectangle" : "rectangle" +>s.width * s.height : number +>s.width : number +>s : Rectangle +>width : number +>s.height : number +>s : Rectangle +>height : number + + case "circle": return Math.PI * s.radius * s.radius; +>"circle" : "circle" +>Math.PI * s.radius * s.radius : number +>Math.PI * s.radius : number +>Math.PI : number +>Math : Math +>PI : number +>s.radius : number +>s : Circle +>radius : number +>s.radius : number +>s : Circle +>radius : number + } +} + From 965c4ca0a25319d2c22f0f4f83ad69fa622baff1 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 29 Aug 2016 09:41:16 -0700 Subject: [PATCH 4/6] Add test and TODOs for narrowing iterated accesses --- src/compiler/binder.ts | 25 +++++++------- src/compiler/checker.ts | 3 +- ...uardNarrowsIndexedAccessOfKnownProperty.ts | 33 +++++++++++++++---- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index e63519c953bf0..0eb527099a20b 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -609,21 +609,19 @@ namespace ts { } function isNarrowableReference(expr: Expression): boolean { - if (expr.kind === SyntaxKind.ElementAccessExpression) { - const argument = (expr as ElementAccessExpression).argumentExpression; - return argument.kind === SyntaxKind.StringLiteral || argument.kind === SyntaxKind.NumericLiteral || argument.kind === SyntaxKind.EnumMember; + if (expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword) { + return true; } - while (true) { - if (expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword) { - return true; - } - else if (expr.kind === SyntaxKind.PropertyAccessExpression) { - expr = (expr as PropertyAccessExpression).expression; - } - else { - return false; - } + if (expr.kind === SyntaxKind.PropertyAccessExpression) { + return isNarrowableReference((expr as PropertyAccessExpression).expression); + } + if (expr.kind === SyntaxKind.ElementAccessExpression) { + const access = expr as ElementAccessExpression; + const isArgumentLiteral = access.argumentExpression.kind === SyntaxKind.StringLiteral || + access.argumentExpression.kind === SyntaxKind.NumericLiteral; + return isArgumentLiteral && isNarrowableReference(access.expression); } + return false; } function hasNarrowableArgument(expr: CallExpression) { @@ -1737,6 +1735,7 @@ namespace ts { } return checkStrictModeIdentifier(node); case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: if (currentFlow && isNarrowableReference(node)) { node.flowNode = currentFlow; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6791f5bf35074..3a52581370951 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14014,7 +14014,8 @@ namespace ts { for (const decl of indexSymbol.declarations) { const declaration = decl; if (declaration.parameters.length === 1 && declaration.parameters[0].type) { - switch (declaration.parameters[0].type.kind) { + const workaround = 0; + switch (declaration.parameters[workaround].type.kind) { case SyntaxKind.StringKeyword: if (!seenStringIndexer) { seenStringIndexer = true; diff --git a/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts b/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts index 94bb29ac9aef6..539a1accbad28 100644 --- a/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts +++ b/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts @@ -1,26 +1,45 @@ interface Square { - kind: "square"; + sub: {kind: 'square'; }; + /*'0': { + sub: { + under: { + kind: "square" + } + } + };*/ size: number; } interface Rectangle { - kind: "rectangle"; + /*'0': { + sub: { + under: { + kind: "rectangle" + } + } + };*/ + sub: { kind: 'rectangle'; }; width: number; height: number; } interface Circle { - kind: "circle"; + /*'0': { + sub: { + under : { + kind: "circle" + } + } + };*/ + sub: { kind: 'circle'; }; radius: number; } type Shape = Square | Rectangle | Circle; function area(s: Shape) { - // In the following switch statement, the type of s is narrowed in each case clause - // according to the value of the discriminant property, thus allowing the other properties - // of that variant to be accessed without a type assertion. - switch (s['kind']) { + switch(s.sub.kind) { + //switch (s[0].sub['under']['kind']) { case "square": return s.size * s.size; case "rectangle": return s.width * s.height; case "circle": return Math.PI * s.radius * s.radius; From a334e6ae7ced779fce961464f0e9aa423da9018b Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 29 Aug 2016 16:30:37 -0700 Subject: [PATCH 5/6] Narrow nested element accesses of known properties --- src/compiler/binder.ts | 5 +++-- src/compiler/checker.ts | 36 +++++++++++++++++++++++------------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 0eb527099a20b..718ff868d52e7 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -617,8 +617,9 @@ namespace ts { } if (expr.kind === SyntaxKind.ElementAccessExpression) { const access = expr as ElementAccessExpression; - const isArgumentLiteral = access.argumentExpression.kind === SyntaxKind.StringLiteral || - access.argumentExpression.kind === SyntaxKind.NumericLiteral; + const isArgumentLiteral = access.argumentExpression && + (access.argumentExpression.kind === SyntaxKind.StringLiteral || + access.argumentExpression.kind === SyntaxKind.NumericLiteral); return isArgumentLiteral && isNarrowableReference(access.expression); } return false; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3a52581370951..dde99b28fc6b7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7848,11 +7848,25 @@ namespace ts { case SyntaxKind.ThisKeyword: return target.kind === SyntaxKind.ThisKeyword; case SyntaxKind.PropertyAccessExpression: - return target.kind === SyntaxKind.PropertyAccessExpression && - (source).name.text === (target).name.text && - isMatchingReference((source).expression, (target).expression); + case SyntaxKind.ElementAccessExpression: + if (target.kind !== SyntaxKind.PropertyAccessExpression && target.kind !== SyntaxKind.ElementAccessExpression) { + return false; + } + const sourceAccess = source as PropertyAccessExpression | ElementAccessExpression; + const targetAccess = target as PropertyAccessExpression | ElementAccessExpression; + return isMatchingReference(sourceAccess.expression, targetAccess.expression) && + getAccessedPropertyName(sourceAccess) === getAccessedPropertyName(targetAccess); + } + return false; + } + + function getAccessedPropertyName(access: PropertyAccessExpression | ElementAccessExpression, type?: Type | undefined): string { + if (!type) { + type = getTypeOfNode(access.expression); } - return false; + return access.kind === SyntaxKind.PropertyAccessExpression ? + (access as PropertyAccessExpression).name.text : + getPropertyNameForIndexedAccess((access as ElementAccessExpression).argumentExpression, type); } function containsMatchingReference(source: Node, target: Node) { @@ -8456,17 +8470,13 @@ namespace ts { expr.kind !== SyntaxKind.PropertyAccessExpression && expr.kind !== SyntaxKind.ElementAccessExpression) { return false; } - const name = expr.kind === SyntaxKind.PropertyAccessExpression ? - (expr as PropertyAccessExpression).name.text : - getPropertyNameForIndexedAccess((expr as ElementAccessExpression).argumentExpression, declaredType); - return isMatchingReference(reference, (expr as PropertyAccessExpression | ElementAccessExpression).expression) && - isDiscriminantProperty(declaredType, name); + const access = expr as PropertyAccessExpression | ElementAccessExpression; + const name = getAccessedPropertyName(access, declaredType); + return isMatchingReference(reference, access.expression) && isDiscriminantProperty(declaredType, name); } function narrowTypeByDiscriminant(type: Type, access: PropertyAccessExpression | ElementAccessExpression, narrowType: (t: Type) => Type): Type { - const propName = access.kind === SyntaxKind.PropertyAccessExpression ? - (access as PropertyAccessExpression).name.text : - getPropertyNameForIndexedAccess((access as ElementAccessExpression).argumentExpression, type); + const propName = getAccessedPropertyName(access, type); const propType = getTypeOfPropertyOfType(type, propName); const narrowedPropType = propType && narrowType(propType); return propType === narrowedPropType ? type : filterType(type, t => isTypeComparableTo(getTypeOfPropertyOfType(t, propName), narrowedPropType)); @@ -10933,7 +10943,7 @@ namespace ts { const prop = getPropertyOfType(objectType, name); if (prop) { getNodeLinks(node).resolvedSymbol = prop; - return getTypeOfSymbol(prop); + return getFlowTypeOfReference(node, getTypeOfSymbol(prop), /*assumeInitialized*/ true, /*flowContainer*/ undefined); } else if (isConstEnum) { error(node.argumentExpression, Diagnostics.Property_0_does_not_exist_on_const_enum_1, name, symbolToString(objectType.symbol)); From f46117f7f1e410079590c9cf6fc47aa6908dbc56 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 29 Aug 2016 16:31:30 -0700 Subject: [PATCH 6/6] Test nested narrowing of element accesses --- ...uardNarrowsIndexedAccessOfKnownProperty.js | 35 +++-- ...arrowsIndexedAccessOfKnownProperty.symbols | 117 ++++++++++++++-- ...dNarrowsIndexedAccessOfKnownProperty.types | 126 +++++++++++++++++- ...uardNarrowsIndexedAccessOfKnownProperty.ts | 50 +++---- 4 files changed, 272 insertions(+), 56 deletions(-) diff --git a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js index fd88e201c969a..b6d02a7c4e407 100644 --- a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js @@ -16,27 +16,44 @@ interface Circle { } type Shape = Square | Rectangle | Circle; - -function area(s: Shape) { - // In the following switch statement, the type of s is narrowed in each case clause - // according to the value of the discriminant property, thus allowing the other properties - // of that variant to be accessed without a type assertion. - switch (s['kind']) { +interface Subshape { + "0": { + sub: { + under: { + shape: Shape; + } + } + } +} +function area(s: Shape): number { + switch(s['kind']) { case "square": return s.size * s.size; case "rectangle": return s.width * s.height; case "circle": return Math.PI * s.radius * s.radius; } } + +function subarea(s: Subshape): number { + switch(s[0]["sub"].under["shape"]["kind"]) { + case "square": return s[0].sub.under.shape.size * s[0].sub.under.shape.size; + case "rectangle": return s[0]["sub"]["under"]["shape"]["width"] * s[0]["sub"]["under"]["shape"].height; + case "circle": return Math.PI * s[0].sub.under["shape"].radius * s[0]["sub"].under.shape["radius"]; + } +} //// [typeGuardNarrowsIndexedAccessOfKnownProperty.js] function area(s) { - // In the following switch statement, the type of s is narrowed in each case clause - // according to the value of the discriminant property, thus allowing the other properties - // of that variant to be accessed without a type assertion. switch (s['kind']) { case "square": return s.size * s.size; case "rectangle": return s.width * s.height; case "circle": return Math.PI * s.radius * s.radius; } } +function subarea(s) { + switch (s[0]["sub"].under["shape"]["kind"]) { + case "square": return s[0].sub.under.shape.size * s[0].sub.under.shape.size; + case "rectangle": return s[0]["sub"]["under"]["shape"]["width"] * s[0]["sub"]["under"]["shape"].height; + case "circle": return Math.PI * s[0].sub.under["shape"].radius * s[0]["sub"].under.shape["radius"]; + } +} diff --git a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.symbols b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.symbols index dd78d1ed062cc..7816587fef8f9 100644 --- a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.symbols +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.symbols @@ -38,32 +38,46 @@ type Shape = Square | Rectangle | Circle; >Rectangle : Symbol(Rectangle, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 3, 1)) >Circle : Symbol(Circle, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 9, 1)) -function area(s: Shape) { ->area : Symbol(area, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 41)) ->s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) +interface Subshape { +>Subshape : Symbol(Subshape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 41)) + + "0": { + sub: { +>sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 10)) + + under: { +>under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 19, 14)) + + shape: Shape; +>shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 20, 20)) +>Shape : Symbol(Shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 14, 1)) + } + } + } +} +function area(s: Shape): number { +>area : Symbol(area, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 25, 1)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 26, 14)) >Shape : Symbol(Shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 14, 1)) - // In the following switch statement, the type of s is narrowed in each case clause - // according to the value of the discriminant property, thus allowing the other properties - // of that variant to be accessed without a type assertion. - switch (s['kind']) { ->s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) + switch(s['kind']) { +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 26, 14)) >'kind' : Symbol(kind, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 18), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 5, 21), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 11, 18)) case "square": return s.size * s.size; >s.size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) ->s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 26, 14)) >size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) >s.size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) ->s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 26, 14)) >size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) case "rectangle": return s.width * s.height; >s.width : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 6, 22)) ->s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 26, 14)) >width : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 6, 22)) >s.height : Symbol(Rectangle.height, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 7, 18)) ->s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 26, 14)) >height : Symbol(Rectangle.height, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 7, 18)) case "circle": return Math.PI * s.radius * s.radius; @@ -71,11 +85,86 @@ function area(s: Shape) { >Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) >PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) >s.radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 19)) ->s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 26, 14)) >radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 19)) >s.radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 19)) ->s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 14)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 26, 14)) +>radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 19)) + } +} + +function subarea(s: Subshape): number { +>subarea : Symbol(subarea, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 32, 1)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 34, 17)) +>Subshape : Symbol(Subshape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 41)) + + switch(s[0]["sub"].under["shape"]["kind"]) { +>s[0]["sub"].under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 19, 14)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 34, 17)) +>0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) +>"sub" : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 10)) +>under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 19, 14)) +>"shape" : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 20, 20)) +>"kind" : Symbol(kind, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 18), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 5, 21), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 11, 18)) + + case "square": return s[0].sub.under.shape.size * s[0].sub.under.shape.size; +>s[0].sub.under.shape.size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) +>s[0].sub.under.shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 20, 20)) +>s[0].sub.under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 19, 14)) +>s[0].sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 10)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 34, 17)) +>0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) +>sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 10)) +>under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 19, 14)) +>shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 20, 20)) +>size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) +>s[0].sub.under.shape.size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) +>s[0].sub.under.shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 20, 20)) +>s[0].sub.under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 19, 14)) +>s[0].sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 10)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 34, 17)) +>0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) +>sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 10)) +>under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 19, 14)) +>shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 20, 20)) +>size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) + + case "rectangle": return s[0]["sub"]["under"]["shape"]["width"] * s[0]["sub"]["under"]["shape"].height; +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 34, 17)) +>0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) +>"sub" : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 10)) +>"under" : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 19, 14)) +>"shape" : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 20, 20)) +>"width" : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 6, 22)) +>s[0]["sub"]["under"]["shape"].height : Symbol(Rectangle.height, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 7, 18)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 34, 17)) +>0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) +>"sub" : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 10)) +>"under" : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 19, 14)) +>"shape" : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 20, 20)) +>height : Symbol(Rectangle.height, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 7, 18)) + + case "circle": return Math.PI * s[0].sub.under["shape"].radius * s[0]["sub"].under.shape["radius"]; +>Math.PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>s[0].sub.under["shape"].radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 19)) +>s[0].sub.under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 19, 14)) +>s[0].sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 10)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 34, 17)) +>0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) +>sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 10)) +>under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 19, 14)) +>"shape" : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 20, 20)) >radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 19)) +>s[0]["sub"].under.shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 20, 20)) +>s[0]["sub"].under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 19, 14)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 34, 17)) +>0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) +>"sub" : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 18, 10)) +>under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 19, 14)) +>shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 20, 20)) +>"radius" : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 19)) } } diff --git a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types index 794c767afe7ae..1f062d7935f18 100644 --- a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types @@ -38,15 +38,29 @@ type Shape = Square | Rectangle | Circle; >Rectangle : Rectangle >Circle : Circle -function area(s: Shape) { +interface Subshape { +>Subshape : Subshape + + "0": { + sub: { +>sub : { under: { shape: Shape; }; } + + under: { +>under : { shape: Shape; } + + shape: Shape; +>shape : Shape +>Shape : Shape + } + } + } +} +function area(s: Shape): number { >area : (s: Shape) => number >s : Shape >Shape : Shape - // In the following switch statement, the type of s is narrowed in each case clause - // according to the value of the discriminant property, thus allowing the other properties - // of that variant to be accessed without a type assertion. - switch (s['kind']) { + switch(s['kind']) { >s['kind'] : "square" | "rectangle" | "circle" >s : Shape >'kind' : string @@ -87,3 +101,105 @@ function area(s: Shape) { } } +function subarea(s: Subshape): number { +>subarea : (s: Subshape) => number +>s : Subshape +>Subshape : Subshape + + switch(s[0]["sub"].under["shape"]["kind"]) { +>s[0]["sub"].under["shape"]["kind"] : "square" | "rectangle" | "circle" +>s[0]["sub"].under["shape"] : Shape +>s[0]["sub"].under : { shape: Shape; } +>s[0]["sub"] : { under: { shape: Shape; }; } +>s[0] : { sub: { under: { shape: Shape; }; }; } +>s : Subshape +>0 : number +>"sub" : string +>under : { shape: Shape; } +>"shape" : string +>"kind" : string + + case "square": return s[0].sub.under.shape.size * s[0].sub.under.shape.size; +>"square" : "square" +>s[0].sub.under.shape.size * s[0].sub.under.shape.size : number +>s[0].sub.under.shape.size : number +>s[0].sub.under.shape : Square +>s[0].sub.under : { shape: Shape; } +>s[0].sub : { under: { shape: Shape; }; } +>s[0] : { sub: { under: { shape: Shape; }; }; } +>s : Subshape +>0 : number +>sub : { under: { shape: Shape; }; } +>under : { shape: Shape; } +>shape : Square +>size : number +>s[0].sub.under.shape.size : number +>s[0].sub.under.shape : Square +>s[0].sub.under : { shape: Shape; } +>s[0].sub : { under: { shape: Shape; }; } +>s[0] : { sub: { under: { shape: Shape; }; }; } +>s : Subshape +>0 : number +>sub : { under: { shape: Shape; }; } +>under : { shape: Shape; } +>shape : Square +>size : number + + case "rectangle": return s[0]["sub"]["under"]["shape"]["width"] * s[0]["sub"]["under"]["shape"].height; +>"rectangle" : "rectangle" +>s[0]["sub"]["under"]["shape"]["width"] * s[0]["sub"]["under"]["shape"].height : number +>s[0]["sub"]["under"]["shape"]["width"] : number +>s[0]["sub"]["under"]["shape"] : Rectangle +>s[0]["sub"]["under"] : { shape: Shape; } +>s[0]["sub"] : { under: { shape: Shape; }; } +>s[0] : { sub: { under: { shape: Shape; }; }; } +>s : Subshape +>0 : number +>"sub" : string +>"under" : string +>"shape" : string +>"width" : string +>s[0]["sub"]["under"]["shape"].height : number +>s[0]["sub"]["under"]["shape"] : Rectangle +>s[0]["sub"]["under"] : { shape: Shape; } +>s[0]["sub"] : { under: { shape: Shape; }; } +>s[0] : { sub: { under: { shape: Shape; }; }; } +>s : Subshape +>0 : number +>"sub" : string +>"under" : string +>"shape" : string +>height : number + + case "circle": return Math.PI * s[0].sub.under["shape"].radius * s[0]["sub"].under.shape["radius"]; +>"circle" : "circle" +>Math.PI * s[0].sub.under["shape"].radius * s[0]["sub"].under.shape["radius"] : number +>Math.PI * s[0].sub.under["shape"].radius : number +>Math.PI : number +>Math : Math +>PI : number +>s[0].sub.under["shape"].radius : number +>s[0].sub.under["shape"] : Circle +>s[0].sub.under : { shape: Shape; } +>s[0].sub : { under: { shape: Shape; }; } +>s[0] : { sub: { under: { shape: Shape; }; }; } +>s : Subshape +>0 : number +>sub : { under: { shape: Shape; }; } +>under : { shape: Shape; } +>"shape" : string +>radius : number +>s[0]["sub"].under.shape["radius"] : number +>s[0]["sub"].under.shape : Circle +>s[0]["sub"].under : { shape: Shape; } +>s[0]["sub"] : { under: { shape: Shape; }; } +>s[0] : { sub: { under: { shape: Shape; }; }; } +>s : Subshape +>0 : number +>"sub" : string +>under : { shape: Shape; } +>shape : Circle +>"radius" : string + } +} + diff --git a/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts b/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts index 539a1accbad28..f71a5f3c9d864 100644 --- a/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts +++ b/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts @@ -1,47 +1,41 @@ interface Square { - sub: {kind: 'square'; }; - /*'0': { - sub: { - under: { - kind: "square" - } - } - };*/ + kind: "square"; size: number; } interface Rectangle { - /*'0': { - sub: { - under: { - kind: "rectangle" - } - } - };*/ - sub: { kind: 'rectangle'; }; + kind: "rectangle"; width: number; height: number; } interface Circle { - /*'0': { - sub: { - under : { - kind: "circle" - } - } - };*/ - sub: { kind: 'circle'; }; + kind: "circle"; radius: number; } type Shape = Square | Rectangle | Circle; - -function area(s: Shape) { - switch(s.sub.kind) { - //switch (s[0].sub['under']['kind']) { +interface Subshape { + "0": { + sub: { + under: { + shape: Shape; + } + } + } +} +function area(s: Shape): number { + switch(s['kind']) { case "square": return s.size * s.size; case "rectangle": return s.width * s.height; case "circle": return Math.PI * s.radius * s.radius; } } + +function subarea(s: Subshape): number { + switch(s[0]["sub"].under["shape"]["kind"]) { + case "square": return s[0].sub.under.shape.size * s[0].sub.under.shape.size; + case "rectangle": return s[0]["sub"]["under"]["shape"]["width"] * s[0]["sub"]["under"]["shape"].height; + case "circle": return Math.PI * s[0].sub.under["shape"].radius * s[0]["sub"].under.shape["radius"]; + } +}