From 2ab8989d8d7dab0c750748a03cb0546be2e83267 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 13 Aug 2018 13:44:27 -0700 Subject: [PATCH 1/7] Narrow literal element accesses This means that, for example, the tuple `[number, string?]` allows its second element to be narrowed with element access: ```ts export function f(pair: [number, string?]): string { return pair[1] ? pair[1] : 'nope'; } ``` --- src/compiler/binder.ts | 20 +- src/compiler/checker.ts | 55 +++- ...uardNarrowsIndexedAccessOfKnownProperty.js | 123 +++++++ ...arrowsIndexedAccessOfKnownProperty.symbols | 267 ++++++++++++++++ ...dNarrowsIndexedAccessOfKnownProperty.types | 301 ++++++++++++++++++ ...uardNarrowsIndexedAccessOfKnownProperty.ts | 80 +++++ 6 files changed, 827 insertions(+), 19 deletions(-) create mode 100644 tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js create mode 100644 tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.symbols create mode 100644 tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types create mode 100644 tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5f5145d78243a..24982d55796ac 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -723,6 +723,7 @@ namespace ts { case SyntaxKind.Identifier: case SyntaxKind.ThisKeyword: case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: return isNarrowableReference(expr); case SyntaxKind.CallExpression: return hasNarrowableArgument(expr); @@ -737,10 +738,20 @@ namespace ts { } function isNarrowableReference(expr: Expression): boolean { - return expr.kind === SyntaxKind.Identifier || - expr.kind === SyntaxKind.ThisKeyword || - expr.kind === SyntaxKind.SuperKeyword || - expr.kind === SyntaxKind.PropertyAccessExpression && isNarrowableReference((expr).expression); + if (expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword) { + return true; + } + 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 && + (access.argumentExpression.kind === SyntaxKind.StringLiteral || + access.argumentExpression.kind === SyntaxKind.NumericLiteral); + return isArgumentLiteral && isNarrowableReference(access.expression); + } + return false; } function hasNarrowableArgument(expr: CallExpression) { @@ -2066,6 +2077,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 2eed799837918..7ea28c4ba2c09 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9155,7 +9155,8 @@ namespace ts { getNodeLinks(accessNode!).resolvedSymbol = prop; } } - return getTypeOfSymbol(prop); + const t = getTypeOfSymbol(prop); + return accessExpression ? getFlowTypeOfReference(accessExpression, t) : t; } if (isTupleType(objectType)) { const restType = getRestTypeOfTupleType(objectType); @@ -13767,9 +13768,14 @@ namespace ts { case SyntaxKind.SuperKeyword: return target.kind === SyntaxKind.SuperKeyword; case SyntaxKind.PropertyAccessExpression: - return target.kind === SyntaxKind.PropertyAccessExpression && - (source).name.escapedText === (target).name.escapedText && - 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 getAccessedPropertyName(sourceAccess) === getAccessedPropertyName(targetAccess) + && isMatchingReference(sourceAccess.expression, targetAccess.expression); case SyntaxKind.BindingElement: if (target.kind !== SyntaxKind.PropertyAccessExpression) return false; const t = target as PropertyAccessExpression; @@ -13785,6 +13791,12 @@ namespace ts { return false; } + function getAccessedPropertyName(access: PropertyAccessExpression | ElementAccessExpression): __String | undefined { + return isPropertyAccessExpression(access) ? access.name.escapedText : + isStringLiteral(access.argumentExpression) || isNumericLiteral(access.argumentExpression) ? escapeLeadingUnderscores(access.argumentExpression.text) : + undefined; + } + function containsMatchingReference(source: Node, target: Node) { while (source.kind === SyntaxKind.PropertyAccessExpression) { source = (source).expression; @@ -14425,7 +14437,7 @@ namespace ts { } } else if (flags & FlowFlags.Start) { - // Check if we should continue with the control flow of the containing function. + // Check if we should continue with the control flow of the containing function. TODO: Should probably make this access element access too const container = (flow).container; if (container && container !== flowContainer && reference.kind !== SyntaxKind.PropertyAccessExpression && reference.kind !== SyntaxKind.ThisKeyword) { flow = container.flowNode!; @@ -14544,7 +14556,9 @@ namespace ts { type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); } else if (isMatchingReferenceDiscriminant(expr, type)) { - 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)); } @@ -14660,14 +14674,23 @@ namespace ts { } function isMatchingReferenceDiscriminant(expr: Expression, computedType: Type) { - return expr.kind === SyntaxKind.PropertyAccessExpression && - computedType.flags & TypeFlags.Union && - isMatchingReference(reference, (expr).expression) && - isDiscriminantProperty(computedType, (expr).name.escapedText); + if (!(computedType.flags & TypeFlags.Union) || + expr.kind !== SyntaxKind.PropertyAccessExpression && expr.kind !== SyntaxKind.ElementAccessExpression) { + return false; + } + const access = expr as PropertyAccessExpression | ElementAccessExpression; + const name = getAccessedPropertyName(access); + if (!name) { + return false; + } + return isMatchingReference(reference, access.expression) && isDiscriminantProperty(computedType, name); } - function narrowTypeByDiscriminant(type: Type, propAccess: PropertyAccessExpression, narrowType: (t: Type) => Type): Type { - const propName = propAccess.name.escapedText; + function narrowTypeByDiscriminant(type: Type, access: PropertyAccessExpression | ElementAccessExpression, narrowType: (t: Type) => Type): Type { + const propName = getAccessedPropertyName(access); + if (!propName) { + return type; + } const propType = getTypeOfPropertyOfType(type, propName); const narrowedPropType = propType && narrowType(propType); return propType === narrowedPropType ? type : filterType(type, t => isTypeComparableTo(getTypeOfPropertyOfType(t, propName)!, narrowedPropType!)); @@ -14678,7 +14701,7 @@ namespace ts { return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy); } if (isMatchingReferenceDiscriminant(expr, declaredType)) { - 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; @@ -14729,10 +14752,10 @@ namespace ts { return narrowTypeByEquality(type, operator, left, assumeTrue); } if (isMatchingReferenceDiscriminant(left, declaredType)) { - return narrowTypeByDiscriminant(type, left, t => narrowTypeByEquality(t, operator, right, assumeTrue)); + return narrowTypeByDiscriminant(type, left, t => narrowTypeByEquality(t, operator, right, assumeTrue)); } if (isMatchingReferenceDiscriminant(right, declaredType)) { - 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; @@ -14971,6 +14994,7 @@ namespace ts { case SyntaxKind.ThisKeyword: case SyntaxKind.SuperKeyword: case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: return narrowTypeByTruthiness(type, expr, assumeTrue); case SyntaxKind.CallExpression: return narrowTypeByTypePredicate(type, expr, assumeTrue); @@ -22413,6 +22437,7 @@ namespace ts { for (const decl of indexSymbol.declarations) { const declaration = decl; if (declaration.parameters.length === 1 && declaration.parameters[0].type) { + // const workaround = 0; switch (declaration.parameters[0].type!.kind) { case SyntaxKind.StringKeyword: if (!seenStringIndexer) { diff --git a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js new file mode 100644 index 0000000000000..03e8fbb2786d2 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js @@ -0,0 +1,123 @@ +//// [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; +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"]; + } +} + +interface X { + 0: "xx", + 1: number +} + +interface Y { + 0: "yy", + 1: string +} + +type A = ["aa", number]; +type B = ["bb", string]; + +type Z = X | Y; + +type C = A | B; + +function check(z: Z, c: C) { + z[0] // fine, typescript sees "xx" | "yy" + switch (z[0]) { + case "xx": + var xx: number = z[1] // should be number + break; + case "yy": + var yy: string = z[1] // should be string + break; + } + c[0] // fine, typescript sees "xx" | "yy" + switch (c[0]) { + case "aa": + var aa: number = c[1] // should be number + break; + case "bb": + var bb: string = c[1] // should be string + break; + } +} + +export function g(pair: [number, string?]): string { + return pair[1] ? pair[1] : 'nope'; +} + + +//// [typeGuardNarrowsIndexedAccessOfKnownProperty.js] +"use strict"; +exports.__esModule = true; +function area(s) { + 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"]; + } +} +function check(z, c) { + z[0]; // fine, typescript sees "xx" | "yy" + switch (z[0]) { + case "xx": + var xx = z[1]; // should be number + break; + case "yy": + var yy = z[1]; // should be string + break; + } + c[0]; // fine, typescript sees "xx" | "yy" + switch (c[0]) { + case "aa": + var aa = c[1]; // should be number + break; + case "bb": + var bb = c[1]; // should be string + break; + } +} +function g(pair) { + return pair[1] ? pair[1] : 'nope'; +} +exports.g = g; diff --git a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.symbols b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.symbols new file mode 100644 index 0000000000000..240e42e6d1e1f --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.symbols @@ -0,0 +1,267 @@ +=== 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, 4, 22)) + + width: number; +>width : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 5, 22)) + + height: number; +>height : Symbol(Rectangle.height, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 6, 18)) +} + interface Circle { +>Circle : Symbol(Circle, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 8, 1)) + + kind: "circle"; +>kind : Symbol(Circle.kind, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 9, 19)) + + radius: number; +>radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 19)) +} + type Shape = Square | Rectangle | Circle; +>Shape : Symbol(Shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 1)) +>Square : Symbol(Square, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 0)) +>Rectangle : Symbol(Rectangle, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 3, 1)) +>Circle : Symbol(Circle, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 8, 1)) + +interface Subshape { +>Subshape : Symbol(Subshape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 13, 42)) + + "0": { +>"0" : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 14, 20)) + + sub: { +>sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) + + under: { +>under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) + + shape: Shape; +>shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) +>Shape : Symbol(Shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 1)) + } + } + } +} +function area(s: Shape): number { +>area : Symbol(area, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 22, 1)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) +>Shape : Symbol(Shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 1)) + + switch(s['kind']) { +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) +>'kind' : Symbol(kind, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 18), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 4, 22), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 9, 19)) + + case "square": return s.size * s['size']; +>s.size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) +>size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 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, 5, 22)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) +>width : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 5, 22)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) +>'height' : Symbol(Rectangle.height, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 6, 18)) + + case "circle": return Math.PI * s['radius'] * s.radius; +>Math.PI : Symbol(Math.PI, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>PI : Symbol(Math.PI, Decl(lib.es5.d.ts, --, --)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) +>'radius' : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 19)) +>s.radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 19)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) +>radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 19)) + } +} +function subarea(s: Subshape): number { +>subarea : Symbol(subarea, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 29, 1)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 30, 17)) +>Subshape : Symbol(Subshape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 13, 42)) + + switch(s[0]["sub"].under["shape"]["kind"]) { +>s[0]["sub"].under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 30, 17)) +>0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 14, 20)) +>"sub" : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) +>under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) +>"shape" : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) +>"kind" : Symbol(kind, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 18), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 4, 22), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 9, 19)) + + 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, 17, 20)) +>s[0].sub.under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) +>s[0].sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 30, 17)) +>0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 14, 20)) +>sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) +>under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) +>shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 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, 17, 20)) +>s[0].sub.under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) +>s[0].sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 30, 17)) +>0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 14, 20)) +>sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) +>under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) +>shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 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, 30, 17)) +>0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 14, 20)) +>"sub" : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) +>"under" : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) +>"shape" : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) +>"width" : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 5, 22)) +>s[0]["sub"]["under"]["shape"].height : Symbol(Rectangle.height, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 6, 18)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 30, 17)) +>0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 14, 20)) +>"sub" : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) +>"under" : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) +>"shape" : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) +>height : Symbol(Rectangle.height, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 6, 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.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>PI : Symbol(Math.PI, Decl(lib.es5.d.ts, --, --)) +>s[0].sub.under["shape"].radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 19)) +>s[0].sub.under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) +>s[0].sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 30, 17)) +>0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 14, 20)) +>sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) +>under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) +>"shape" : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) +>radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 19)) +>s[0]["sub"].under.shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) +>s[0]["sub"].under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 30, 17)) +>0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 14, 20)) +>"sub" : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) +>under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) +>shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) +>"radius" : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 19)) + } +} + +interface X { +>X : Symbol(X, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 36, 1)) + + 0: "xx", +>0 : Symbol(X[0], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 38, 13)) + + 1: number +>1 : Symbol(X[1], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 39, 12)) +} + +interface Y { +>Y : Symbol(Y, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 41, 1)) + + 0: "yy", +>0 : Symbol(Y[0], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 43, 13)) + + 1: string +>1 : Symbol(Y[1], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 44, 12)) +} + +type A = ["aa", number]; +>A : Symbol(A, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 46, 1)) + +type B = ["bb", string]; +>B : Symbol(B, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 48, 24)) + +type Z = X | Y; +>Z : Symbol(Z, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 49, 24)) +>X : Symbol(X, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 36, 1)) +>Y : Symbol(Y, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 41, 1)) + +type C = A | B; +>C : Symbol(C, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 51, 15)) +>A : Symbol(A, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 46, 1)) +>B : Symbol(B, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 48, 24)) + +function check(z: Z, c: C) { +>check : Symbol(check, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 53, 15)) +>z : Symbol(z, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 55, 15)) +>Z : Symbol(Z, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 49, 24)) +>c : Symbol(c, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 55, 20)) +>C : Symbol(C, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 51, 15)) + + z[0] // fine, typescript sees "xx" | "yy" +>z : Symbol(z, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 55, 15)) +>0 : Symbol(0, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 38, 13), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 43, 13)) + + switch (z[0]) { +>z : Symbol(z, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 55, 15)) +>0 : Symbol(0, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 38, 13), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 43, 13)) + + case "xx": + var xx: number = z[1] // should be number +>xx : Symbol(xx, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 59, 15)) +>z : Symbol(z, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 55, 15)) +>1 : Symbol(X[1], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 39, 12)) + + break; + case "yy": + var yy: string = z[1] // should be string +>yy : Symbol(yy, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 62, 15)) +>z : Symbol(z, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 55, 15)) +>1 : Symbol(Y[1], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 44, 12)) + + break; + } + c[0] // fine, typescript sees "xx" | "yy" +>c : Symbol(c, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 55, 20)) +>0 : Symbol(0) + + switch (c[0]) { +>c : Symbol(c, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 55, 20)) +>0 : Symbol(0) + + case "aa": + var aa: number = c[1] // should be number +>aa : Symbol(aa, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 68, 15)) +>c : Symbol(c, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 55, 20)) +>1 : Symbol(1) + + break; + case "bb": + var bb: string = c[1] // should be string +>bb : Symbol(bb, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 71, 15)) +>c : Symbol(c, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 55, 20)) +>1 : Symbol(1) + + break; + } +} + +export function g(pair: [number, string?]): string { +>g : Symbol(g, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 74, 1)) +>pair : Symbol(pair, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 76, 18)) + + return pair[1] ? pair[1] : 'nope'; +>pair : Symbol(pair, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 76, 18)) +>1 : Symbol(1) +>pair : Symbol(pair, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 76, 18)) +>1 : Symbol(1) +} + diff --git a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types new file mode 100644 index 0000000000000..3dc4e62930097 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types @@ -0,0 +1,301 @@ +=== tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts === +interface Square { + kind: "square"; +>kind : "square" + + size: number; +>size : number +} + interface Rectangle { + kind: "rectangle"; +>kind : "rectangle" + + width: number; +>width : number + + height: number; +>height : number +} + interface Circle { + kind: "circle"; +>kind : "circle" + + radius: number; +>radius : number +} + type Shape = Square | Rectangle | Circle; +>Shape : Shape + +interface Subshape { + "0": { +>"0" : { sub: { under: { shape: Shape; }; }; } + + sub: { +>sub : { under: { shape: Shape; }; } + + under: { +>under : { shape: Shape; } + + shape: Shape; +>shape : Shape + } + } + } +} +function area(s: Shape): number { +>area : (s: Shape) => number +>s : Shape + + switch(s['kind']) { +>s['kind'] : "square" | "rectangle" | "circle" +>s : Shape +>'kind' : "kind" + + 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' : "size" + + 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' : "height" + + 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' : "radius" +>s.radius : number +>s : Circle +>radius : number + } +} +function subarea(s: Subshape): number { +>subarea : (s: Subshape) => number +>s : 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 : 0 +>"sub" : "sub" +>under : { shape: Shape; } +>"shape" : "shape" +>"kind" : "kind" + + 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 : 0 +>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 : 0 +>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 : 0 +>"sub" : "sub" +>"under" : "under" +>"shape" : "shape" +>"width" : "width" +>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 : 0 +>"sub" : "sub" +>"under" : "under" +>"shape" : "shape" +>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 : 0 +>sub : { under: { shape: Shape; }; } +>under : { shape: Shape; } +>"shape" : "shape" +>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 : 0 +>"sub" : "sub" +>under : { shape: Shape; } +>shape : Circle +>"radius" : "radius" + } +} + +interface X { + 0: "xx", +>0 : "xx" + + 1: number +>1 : number +} + +interface Y { + 0: "yy", +>0 : "yy" + + 1: string +>1 : string +} + +type A = ["aa", number]; +>A : ["aa", number] + +type B = ["bb", string]; +>B : ["bb", string] + +type Z = X | Y; +>Z : Z + +type C = A | B; +>C : C + +function check(z: Z, c: C) { +>check : (z: Z, c: C) => void +>z : Z +>c : C + + z[0] // fine, typescript sees "xx" | "yy" +>z[0] : "xx" | "yy" +>z : Z +>0 : 0 + + switch (z[0]) { +>z[0] : "xx" | "yy" +>z : Z +>0 : 0 + + case "xx": +>"xx" : "xx" + + var xx: number = z[1] // should be number +>xx : number +>z[1] : number +>z : X +>1 : 1 + + break; + case "yy": +>"yy" : "yy" + + var yy: string = z[1] // should be string +>yy : string +>z[1] : string +>z : Y +>1 : 1 + + break; + } + c[0] // fine, typescript sees "xx" | "yy" +>c[0] : "aa" | "bb" +>c : C +>0 : 0 + + switch (c[0]) { +>c[0] : "aa" | "bb" +>c : C +>0 : 0 + + case "aa": +>"aa" : "aa" + + var aa: number = c[1] // should be number +>aa : number +>c[1] : number +>c : ["aa", number] +>1 : 1 + + break; + case "bb": +>"bb" : "bb" + + var bb: string = c[1] // should be string +>bb : string +>c[1] : string +>c : ["bb", string] +>1 : 1 + + break; + } +} + +export function g(pair: [number, string?]): string { +>g : (pair: [number, (string | undefined)?]) => string +>pair : [number, (string | undefined)?] + + return pair[1] ? pair[1] : 'nope'; +>pair[1] ? pair[1] : 'nope' : string +>pair[1] : string | undefined +>pair : [number, (string | undefined)?] +>1 : 1 +>pair[1] : string +>pair : [number, (string | undefined)?] +>1 : 1 +>'nope' : "nope" +} + diff --git a/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts b/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts new file mode 100644 index 0000000000000..d16f1e2e1866e --- /dev/null +++ b/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts @@ -0,0 +1,80 @@ +// @strict: true +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; +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"]; + } +} + +interface X { + 0: "xx", + 1: number +} + +interface Y { + 0: "yy", + 1: string +} + +type A = ["aa", number]; +type B = ["bb", string]; + +type Z = X | Y; + +type C = A | B; + +function check(z: Z, c: C) { + z[0] // fine, typescript sees "xx" | "yy" + switch (z[0]) { + case "xx": + var xx: number = z[1] // should be number + break; + case "yy": + var yy: string = z[1] // should be string + break; + } + c[0] // fine, typescript sees "xx" | "yy" + switch (c[0]) { + case "aa": + var aa: number = c[1] // should be number + break; + case "bb": + var bb: string = c[1] // should be string + break; + } +} + +export function g(pair: [number, string?]): string { + return pair[1] ? pair[1] : 'nope'; +} From 1902f3046a4c57a3cc96af072ffadf759bdcc9f8 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 13 Aug 2018 13:52:07 -0700 Subject: [PATCH 2/7] Update baselines --- .../reference/constDeclarations-access3.types | 34 +++++++++---------- .../reference/constDeclarations-access5.types | 34 +++++++++---------- .../unionTypeWithIndexSignature.errors.txt | 5 ++- .../unionTypeWithIndexSignature.types | 2 +- 4 files changed, 39 insertions(+), 36 deletions(-) diff --git a/tests/baselines/reference/constDeclarations-access3.types b/tests/baselines/reference/constDeclarations-access3.types index 3e3bd071904fe..af8273ff0d232 100644 --- a/tests/baselines/reference/constDeclarations-access3.types +++ b/tests/baselines/reference/constDeclarations-access3.types @@ -135,9 +135,9 @@ M["x"] = 0; var a = M.x + 1; >a : number >M.x + 1 : number ->M.x : number +>M.x : 0 >M : typeof M ->x : number +>x : 0 >1 : 1 function f(v: number) { } @@ -147,43 +147,43 @@ function f(v: number) { } f(M.x); >f(M.x) : void >f : (v: number) => void ->M.x : number +>M.x : 0 >M : typeof M ->x : number +>x : 0 if (M.x) { } ->M.x : number +>M.x : 0 >M : typeof M ->x : number +>x : 0 M.x; ->M.x : number +>M.x : 0 >M : typeof M ->x : number +>x : 0 (M.x); ->(M.x) : number ->M.x : number +>(M.x) : 0 +>M.x : 0 >M : typeof M ->x : number +>x : 0 -M.x; >-M.x : number ->M.x : number +>M.x : 0 >M : typeof M ->x : number +>x : 0 +M.x; >+M.x : number ->M.x : number +>M.x : 0 >M : typeof M ->x : number +>x : 0 M.x.toString(); >M.x.toString() : string >M.x.toString : (radix?: number) => string ->M.x : number +>M.x : 0 >M : typeof M ->x : number +>x : 0 >toString : (radix?: number) => string diff --git a/tests/baselines/reference/constDeclarations-access5.types b/tests/baselines/reference/constDeclarations-access5.types index 4b1902cf57d2f..3cf15ca6a60ac 100644 --- a/tests/baselines/reference/constDeclarations-access5.types +++ b/tests/baselines/reference/constDeclarations-access5.types @@ -134,9 +134,9 @@ m["x"] = 0; var a = m.x + 1; >a : number >m.x + 1 : number ->m.x : number +>m.x : 0 >m : typeof m ->x : number +>x : 0 >1 : 1 function f(v: number) { } @@ -146,44 +146,44 @@ function f(v: number) { } f(m.x); >f(m.x) : void >f : (v: number) => void ->m.x : number +>m.x : 0 >m : typeof m ->x : number +>x : 0 if (m.x) { } ->m.x : number +>m.x : 0 >m : typeof m ->x : number +>x : 0 m.x; ->m.x : number +>m.x : 0 >m : typeof m ->x : number +>x : 0 (m.x); ->(m.x) : number ->m.x : number +>(m.x) : 0 +>m.x : 0 >m : typeof m ->x : number +>x : 0 -m.x; >-m.x : number ->m.x : number +>m.x : 0 >m : typeof m ->x : number +>x : 0 +m.x; >+m.x : number ->m.x : number +>m.x : 0 >m : typeof m ->x : number +>x : 0 m.x.toString(); >m.x.toString() : string >m.x.toString : (radix?: number) => string ->m.x : number +>m.x : 0 >m : typeof m ->x : number +>x : 0 >toString : (radix?: number) => string === tests/cases/compiler/constDeclarations_access_1.ts === diff --git a/tests/baselines/reference/unionTypeWithIndexSignature.errors.txt b/tests/baselines/reference/unionTypeWithIndexSignature.errors.txt index e95a184d15a5b..7f4675b0373a3 100644 --- a/tests/baselines/reference/unionTypeWithIndexSignature.errors.txt +++ b/tests/baselines/reference/unionTypeWithIndexSignature.errors.txt @@ -1,12 +1,13 @@ tests/cases/conformance/types/union/unionTypeWithIndexSignature.ts(11,3): error TS2339: Property 'bar' does not exist on type 'Missing'. Property 'bar' does not exist on type '{ [s: string]: string; }'. tests/cases/conformance/types/union/unionTypeWithIndexSignature.ts(14,4): error TS2540: Cannot assign to 'foo' because it is a constant or a read-only property. +tests/cases/conformance/types/union/unionTypeWithIndexSignature.ts(18,1): error TS2322: Type '"ok"' is not assignable to type 'number'. tests/cases/conformance/types/union/unionTypeWithIndexSignature.ts(24,1): error TS7017: Element implicitly has an 'any' type because type 'Both' has no index signature. tests/cases/conformance/types/union/unionTypeWithIndexSignature.ts(25,1): error TS2322: Type '"not ok"' is not assignable to type 'number'. tests/cases/conformance/types/union/unionTypeWithIndexSignature.ts(26,1): error TS7017: Element implicitly has an 'any' type because type 'Both' has no index signature. -==== tests/cases/conformance/types/union/unionTypeWithIndexSignature.ts (5 errors) ==== +==== tests/cases/conformance/types/union/unionTypeWithIndexSignature.ts (6 errors) ==== type Two = { foo: { bar: true }, baz: true } | { [s: string]: string }; declare var u: Two u.foo = 'bye' @@ -30,6 +31,8 @@ tests/cases/conformance/types/union/unionTypeWithIndexSignature.ts(26,1): error declare var num: Num num[0] = 1 num['0'] = 'ok' + ~~~~~~~~ +!!! error TS2322: Type '"ok"' is not assignable to type 'number'. const sym = Symbol() type Both = { s: number, '0': number, [sym]: boolean } | { [n: number]: number, [s: string]: string | number } declare var both: Both diff --git a/tests/baselines/reference/unionTypeWithIndexSignature.types b/tests/baselines/reference/unionTypeWithIndexSignature.types index 417d62e706e9a..a699d51391cc2 100644 --- a/tests/baselines/reference/unionTypeWithIndexSignature.types +++ b/tests/baselines/reference/unionTypeWithIndexSignature.types @@ -96,7 +96,7 @@ num[0] = 1 num['0'] = 'ok' >num['0'] = 'ok' : "ok" ->num['0'] : string | number +>num['0'] : number >num : Num >'0' : "0" >'ok' : "ok" From 964cebdd1f8242cafccd76f45e379815b6d90f8c Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 13 Aug 2018 13:54:18 -0700 Subject: [PATCH 3/7] Cleanup --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7ea28c4ba2c09..c2c4587e84837 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14437,7 +14437,7 @@ namespace ts { } } else if (flags & FlowFlags.Start) { - // Check if we should continue with the control flow of the containing function. TODO: Should probably make this access element access too + // Check if we should continue with the control flow of the containing function. const container = (flow).container; if (container && container !== flowContainer && reference.kind !== SyntaxKind.PropertyAccessExpression && reference.kind !== SyntaxKind.ThisKeyword) { flow = container.flowNode!; From 7f07bfebde28f00bef66f988c3de0af479f9aa1d Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 13 Aug 2018 13:54:59 -0700 Subject: [PATCH 4/7] More cleanup --- src/compiler/checker.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c2c4587e84837..c610af415dca9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22437,7 +22437,6 @@ namespace ts { for (const decl of indexSymbol.declarations) { const declaration = decl; if (declaration.parameters.length === 1 && declaration.parameters[0].type) { - // const workaround = 0; switch (declaration.parameters[0].type!.kind) { case SyntaxKind.StringKeyword: if (!seenStringIndexer) { From 567e323534133aa8b8d9ac4493634198edbfe597 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 13 Aug 2018 14:04:27 -0700 Subject: [PATCH 5/7] Test dashes in property names --- src/compiler/checker.ts | 5 +- ...uardNarrowsIndexedAccessOfKnownProperty.js | 24 +++---- ...arrowsIndexedAccessOfKnownProperty.symbols | 65 ++++++++++--------- ...dNarrowsIndexedAccessOfKnownProperty.types | 56 ++++++++-------- ...uardNarrowsIndexedAccessOfKnownProperty.ts | 16 ++--- 5 files changed, 87 insertions(+), 79 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c610af415dca9..570f4e5da25cf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14439,7 +14439,10 @@ namespace ts { else if (flags & FlowFlags.Start) { // Check if we should continue with the control flow of the containing function. const container = (flow).container; - if (container && container !== flowContainer && reference.kind !== SyntaxKind.PropertyAccessExpression && reference.kind !== SyntaxKind.ThisKeyword) { + if (container && container !== flowContainer && + reference.kind !== SyntaxKind.PropertyAccessExpression && + reference.kind !== SyntaxKind.ElementAccessExpression && + reference.kind !== SyntaxKind.ThisKeyword) { flow = container.flowNode!; continue; } diff --git a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js index 03e8fbb2786d2..7fa7a59abc553 100644 --- a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js @@ -1,15 +1,15 @@ //// [typeGuardNarrowsIndexedAccessOfKnownProperty.ts] interface Square { - kind: "square"; - size: number; + ["dash-ok"]: "square"; + ["square-size"]: number; } interface Rectangle { - kind: "rectangle"; + ["dash-ok"]: "rectangle"; width: number; height: number; } interface Circle { - kind: "circle"; + ["dash-ok"]: "circle"; radius: number; } type Shape = Square | Rectangle | Circle; @@ -23,15 +23,15 @@ interface Subshape { } } function area(s: Shape): number { - switch(s['kind']) { - case "square": return s.size * s['size']; + switch(s['dash-ok']) { + case "square": return s['square-size'] * s['square-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; + switch(s[0]["sub"].under["shape"]["dash-ok"]) { + case "square": return s[0].sub.under.shape["square-size"] * s[0].sub.under.shape["square-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"]; } @@ -84,15 +84,15 @@ export function g(pair: [number, string?]): string { "use strict"; exports.__esModule = true; function area(s) { - switch (s['kind']) { - case "square": return s.size * s['size']; + switch (s['dash-ok']) { + case "square": return s['square-size'] * s['square-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; + switch (s[0]["sub"].under["shape"]["dash-ok"]) { + case "square": return s[0].sub.under.shape["square-size"] * s[0].sub.under.shape["square-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 240e42e6d1e1f..3a8e434aa3c80 100644 --- a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.symbols +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.symbols @@ -2,20 +2,23 @@ interface Square { >Square : Symbol(Square, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 0)) - kind: "square"; ->kind : Symbol(Square.kind, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 18)) + ["dash-ok"]: "square"; +>["dash-ok"] : Symbol(Square["dash-ok"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 18)) +>"dash-ok" : Symbol(Square["dash-ok"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 18)) - size: number; ->size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) + ["square-size"]: number; +>["square-size"] : Symbol(Square["square-size"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 26)) +>"square-size" : Symbol(Square["square-size"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 26)) } interface Rectangle { >Rectangle : Symbol(Rectangle, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 3, 1)) - kind: "rectangle"; ->kind : Symbol(Rectangle.kind, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 4, 22)) + ["dash-ok"]: "rectangle"; +>["dash-ok"] : Symbol(Rectangle["dash-ok"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 4, 22)) +>"dash-ok" : Symbol(Rectangle["dash-ok"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 4, 22)) width: number; ->width : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 5, 22)) +>width : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 5, 29)) height: number; >height : Symbol(Rectangle.height, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 6, 18)) @@ -23,11 +26,12 @@ interface Square { interface Circle { >Circle : Symbol(Circle, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 8, 1)) - kind: "circle"; ->kind : Symbol(Circle.kind, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 9, 19)) + ["dash-ok"]: "circle"; +>["dash-ok"] : Symbol(Circle["dash-ok"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 9, 19)) +>"dash-ok" : Symbol(Circle["dash-ok"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 9, 19)) radius: number; ->radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 19)) +>radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 26)) } type Shape = Square | Rectangle | Circle; >Shape : Symbol(Shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 1)) @@ -59,21 +63,20 @@ function area(s: Shape): number { >s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) >Shape : Symbol(Shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 12, 1)) - switch(s['kind']) { + switch(s['dash-ok']) { >s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) ->'kind' : Symbol(kind, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 18), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 4, 22), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 9, 19)) +>'dash-ok' : Symbol(["dash-ok"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 18), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 4, 22), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 9, 19)) - case "square": return s.size * s['size']; ->s.size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) + case "square": return s['square-size'] * s['square-size']; >s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) ->size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) +>'square-size' : Symbol(Square["square-size"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 26)) >s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) ->'size' : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) +>'square-size' : Symbol(Square["square-size"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 26)) case "rectangle": return s.width * s['height']; ->s.width : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 5, 22)) +>s.width : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 5, 29)) >s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) ->width : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 5, 22)) +>width : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 5, 29)) >s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) >'height' : Symbol(Rectangle.height, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 6, 18)) @@ -82,10 +85,10 @@ function area(s: Shape): number { >Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >PI : Symbol(Math.PI, Decl(lib.es5.d.ts, --, --)) >s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) ->'radius' : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 19)) ->s.radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 19)) +>'radius' : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 26)) +>s.radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 26)) >s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) ->radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 19)) +>radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 26)) } } function subarea(s: Subshape): number { @@ -93,17 +96,16 @@ function subarea(s: Subshape): number { >s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 30, 17)) >Subshape : Symbol(Subshape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 13, 42)) - switch(s[0]["sub"].under["shape"]["kind"]) { + switch(s[0]["sub"].under["shape"]["dash-ok"]) { >s[0]["sub"].under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) >s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 30, 17)) >0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 14, 20)) >"sub" : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) >under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) >"shape" : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) ->"kind" : Symbol(kind, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 18), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 4, 22), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 9, 19)) +>"dash-ok" : Symbol(["dash-ok"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 18), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 4, 22), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 9, 19)) - 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)) + case "square": return s[0].sub.under.shape["square-size"] * s[0].sub.under.shape["square-size"]; >s[0].sub.under.shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) >s[0].sub.under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) >s[0].sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) @@ -112,8 +114,7 @@ function subarea(s: Subshape): number { >sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) >under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) >shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) ->size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) ->s[0].sub.under.shape.size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) +>"square-size" : Symbol(Square["square-size"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 26)) >s[0].sub.under.shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) >s[0].sub.under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) >s[0].sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) @@ -122,7 +123,7 @@ function subarea(s: Subshape): number { >sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) >under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) >shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) ->size : Symbol(Square.size, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 19)) +>"square-size" : Symbol(Square["square-size"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 26)) case "rectangle": return s[0]["sub"]["under"]["shape"]["width"] * s[0]["sub"]["under"]["shape"].height; >s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 30, 17)) @@ -130,7 +131,7 @@ function subarea(s: Subshape): number { >"sub" : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) >"under" : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) >"shape" : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) ->"width" : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 5, 22)) +>"width" : Symbol(Rectangle.width, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 5, 29)) >s[0]["sub"]["under"]["shape"].height : Symbol(Rectangle.height, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 6, 18)) >s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 30, 17)) >0 : Symbol(Subshape["0"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 14, 20)) @@ -143,7 +144,7 @@ function subarea(s: Subshape): number { >Math.PI : Symbol(Math.PI, Decl(lib.es5.d.ts, --, --)) >Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >PI : Symbol(Math.PI, Decl(lib.es5.d.ts, --, --)) ->s[0].sub.under["shape"].radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 19)) +>s[0].sub.under["shape"].radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 26)) >s[0].sub.under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) >s[0].sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) >s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 30, 17)) @@ -151,7 +152,7 @@ function subarea(s: Subshape): number { >sub : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) >under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) >"shape" : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) ->radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 19)) +>radius : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 26)) >s[0]["sub"].under.shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) >s[0]["sub"].under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) >s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 30, 17)) @@ -159,7 +160,7 @@ function subarea(s: Subshape): number { >"sub" : Symbol(sub, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 15, 10)) >under : Symbol(under, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 16, 14)) >shape : Symbol(shape, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 17, 20)) ->"radius" : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 19)) +>"radius" : Symbol(Circle.radius, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 10, 26)) } } diff --git a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types index 3dc4e62930097..4e3d2e0743e9d 100644 --- a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types @@ -1,14 +1,17 @@ === tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts === interface Square { - kind: "square"; ->kind : "square" + ["dash-ok"]: "square"; +>["dash-ok"] : "square" +>"dash-ok" : "dash-ok" - size: number; ->size : number + ["square-size"]: number; +>["square-size"] : number +>"square-size" : "square-size" } interface Rectangle { - kind: "rectangle"; ->kind : "rectangle" + ["dash-ok"]: "rectangle"; +>["dash-ok"] : "rectangle" +>"dash-ok" : "dash-ok" width: number; >width : number @@ -17,8 +20,9 @@ interface Square { >height : number } interface Circle { - kind: "circle"; ->kind : "circle" + ["dash-ok"]: "circle"; +>["dash-ok"] : "circle" +>"dash-ok" : "dash-ok" radius: number; >radius : number @@ -46,20 +50,20 @@ function area(s: Shape): number { >area : (s: Shape) => number >s : Shape - switch(s['kind']) { ->s['kind'] : "square" | "rectangle" | "circle" + switch(s['dash-ok']) { +>s['dash-ok'] : "square" | "rectangle" | "circle" >s : Shape ->'kind' : "kind" +>'dash-ok' : "dash-ok" - case "square": return s.size * s['size']; + case "square": return s['square-size'] * s['square-size']; >"square" : "square" ->s.size * s['size'] : number ->s.size : number +>s['square-size'] * s['square-size'] : number +>s['square-size'] : number >s : Square ->size : number ->s['size'] : number +>'square-size' : "square-size" +>s['square-size'] : number >s : Square ->'size' : "size" +>'square-size' : "square-size" case "rectangle": return s.width * s['height']; >"rectangle" : "rectangle" @@ -90,8 +94,8 @@ function subarea(s: Subshape): number { >subarea : (s: Subshape) => number >s : Subshape - switch(s[0]["sub"].under["shape"]["kind"]) { ->s[0]["sub"].under["shape"]["kind"] : "square" | "rectangle" | "circle" + switch(s[0]["sub"].under["shape"]["dash-ok"]) { +>s[0]["sub"].under["shape"]["dash-ok"] : "square" | "rectangle" | "circle" >s[0]["sub"].under["shape"] : Shape >s[0]["sub"].under : { shape: Shape; } >s[0]["sub"] : { under: { shape: Shape; }; } @@ -101,12 +105,12 @@ function subarea(s: Subshape): number { >"sub" : "sub" >under : { shape: Shape; } >"shape" : "shape" ->"kind" : "kind" +>"dash-ok" : "dash-ok" - case "square": return s[0].sub.under.shape.size * s[0].sub.under.shape.size; + case "square": return s[0].sub.under.shape["square-size"] * s[0].sub.under.shape["square-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-size"] * s[0].sub.under.shape["square-size"] : number +>s[0].sub.under.shape["square-size"] : number >s[0].sub.under.shape : Square >s[0].sub.under : { shape: Shape; } >s[0].sub : { under: { shape: Shape; }; } @@ -116,8 +120,8 @@ function subarea(s: Subshape): number { >sub : { under: { shape: Shape; }; } >under : { shape: Shape; } >shape : Square ->size : number ->s[0].sub.under.shape.size : number +>"square-size" : "square-size" +>s[0].sub.under.shape["square-size"] : number >s[0].sub.under.shape : Square >s[0].sub.under : { shape: Shape; } >s[0].sub : { under: { shape: Shape; }; } @@ -127,7 +131,7 @@ function subarea(s: Subshape): number { >sub : { under: { shape: Shape; }; } >under : { shape: Shape; } >shape : Square ->size : number +>"square-size" : "square-size" case "rectangle": return s[0]["sub"]["under"]["shape"]["width"] * s[0]["sub"]["under"]["shape"].height; >"rectangle" : "rectangle" diff --git a/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts b/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts index d16f1e2e1866e..9d36f3c8ea929 100644 --- a/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts +++ b/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts @@ -1,15 +1,15 @@ // @strict: true interface Square { - kind: "square"; - size: number; + ["dash-ok"]: "square"; + ["square-size"]: number; } interface Rectangle { - kind: "rectangle"; + ["dash-ok"]: "rectangle"; width: number; height: number; } interface Circle { - kind: "circle"; + ["dash-ok"]: "circle"; radius: number; } type Shape = Square | Rectangle | Circle; @@ -23,15 +23,15 @@ interface Subshape { } } function area(s: Shape): number { - switch(s['kind']) { - case "square": return s.size * s['size']; + switch(s['dash-ok']) { + case "square": return s['square-size'] * s['square-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; + switch(s[0]["sub"].under["shape"]["dash-ok"]) { + case "square": return s[0].sub.under.shape["square-size"] * s[0].sub.under.shape["square-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"]; } From d142cbbb5a71cdb7d336a41407f9174c0426b567 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 13 Aug 2018 15:30:54 -0700 Subject: [PATCH 6/7] More cleanup --- src/compiler/binder.ts | 19 +++++-------------- src/compiler/checker.ts | 18 +++++++++--------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 24982d55796ac..bcefaa9a2d888 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -738,20 +738,11 @@ namespace ts { } function isNarrowableReference(expr: Expression): boolean { - if (expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword) { - return true; - } - 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 && - (access.argumentExpression.kind === SyntaxKind.StringLiteral || - access.argumentExpression.kind === SyntaxKind.NumericLiteral); - return isArgumentLiteral && isNarrowableReference(access.expression); - } - return false; + return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword || + isPropertyAccessExpression(expr) && isNarrowableReference(expr.expression) || + isElementAccessExpression(expr) && expr.argumentExpression && + (isStringLiteral(expr.argumentExpression) || isNumericLiteral(expr.argumentExpression)) && + isNarrowableReference(expr.expression); } function hasNarrowableArgument(expr: CallExpression) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 570f4e5da25cf..abf4ec35dba73 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9155,8 +9155,8 @@ namespace ts { getNodeLinks(accessNode!).resolvedSymbol = prop; } } - const t = getTypeOfSymbol(prop); - return accessExpression ? getFlowTypeOfReference(accessExpression, t) : t; + const propType = getTypeOfSymbol(prop); + return accessExpression ? getFlowTypeOfReference(accessExpression, propType) : propType; } if (isTupleType(objectType)) { const restType = getRestTypeOfTupleType(objectType); @@ -13772,10 +13772,9 @@ namespace ts { if (target.kind !== SyntaxKind.PropertyAccessExpression && target.kind !== SyntaxKind.ElementAccessExpression) { return false; } - const sourceAccess = source as PropertyAccessExpression | ElementAccessExpression; - const targetAccess = target as PropertyAccessExpression | ElementAccessExpression; - return getAccessedPropertyName(sourceAccess) === getAccessedPropertyName(targetAccess) - && isMatchingReference(sourceAccess.expression, targetAccess.expression); + return (isPropertyAccessExpression(target) || isElementAccessExpression(target)) && + getAccessedPropertyName(source as PropertyAccessExpression | ElementAccessExpression) === getAccessedPropertyName(target) && + isMatchingReference((source as PropertyAccessExpression | ElementAccessExpression).expression, target.expression); case SyntaxKind.BindingElement: if (target.kind !== SyntaxKind.PropertyAccessExpression) return false; const t = target as PropertyAccessExpression; @@ -14559,9 +14558,10 @@ namespace ts { type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); } else if (isMatchingReferenceDiscriminant(expr, type)) { - type = narrowTypeByDiscriminant(type, - expr as PropertyAccessExpression | ElementAccessExpression, - 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)); } From 54353a0fc22c5dfa64a259ec549fac7b7f2ccf19 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 13 Aug 2018 15:34:14 -0700 Subject: [PATCH 7/7] Delete undead code --- src/compiler/checker.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index abf4ec35dba73..77e1467ad5382 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13769,9 +13769,6 @@ namespace ts { return target.kind === SyntaxKind.SuperKeyword; case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ElementAccessExpression: - if (target.kind !== SyntaxKind.PropertyAccessExpression && target.kind !== SyntaxKind.ElementAccessExpression) { - return false; - } return (isPropertyAccessExpression(target) || isElementAccessExpression(target)) && getAccessedPropertyName(source as PropertyAccessExpression | ElementAccessExpression) === getAccessedPropertyName(target) && isMatchingReference((source as PropertyAccessExpression | ElementAccessExpression).expression, target.expression);