From 2bfd919b6a21fc424bc06f7c6f5819b58bfd6193 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 15 Aug 2018 09:58:39 -0700 Subject: [PATCH] Narrow on element access of literal (#26424) * 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'; } ``` * Update baselines * Cleanup * More cleanup * Test dashes in property names * More cleanup * Delete undead code --- src/compiler/binder.ts | 11 +- src/compiler/checker.ts | 54 +++- .../reference/constDeclarations-access3.types | 34 +- .../reference/constDeclarations-access5.types | 34 +- ...uardNarrowsIndexedAccessOfKnownProperty.js | 123 +++++++ ...arrowsIndexedAccessOfKnownProperty.symbols | 268 +++++++++++++++ ...dNarrowsIndexedAccessOfKnownProperty.types | 305 ++++++++++++++++++ .../unionTypeWithIndexSignature.errors.txt | 5 +- .../unionTypeWithIndexSignature.types | 2 +- ...uardNarrowsIndexedAccessOfKnownProperty.ts | 80 +++++ 10 files changed, 861 insertions(+), 55 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 5f5145d782..bcefaa9a2d 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,11 @@ 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); + 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) { @@ -2066,6 +2068,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 509f8975eb..c3c982ecfe 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9158,7 +9158,8 @@ namespace ts { getNodeLinks(accessNode!).resolvedSymbol = prop; } } - return getTypeOfSymbol(prop); + const propType = getTypeOfSymbol(prop); + return accessExpression ? getFlowTypeOfReference(accessExpression, propType) : propType; } if (isTupleType(objectType)) { const restType = getRestTypeOfTupleType(objectType); @@ -13778,9 +13779,10 @@ 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: + 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; @@ -13796,6 +13798,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; @@ -14438,7 +14446,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; } @@ -14555,7 +14566,10 @@ 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)); } @@ -14671,14 +14685,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!)); @@ -14689,7 +14712,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; @@ -14740,10 +14763,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; @@ -14982,6 +15005,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); diff --git a/tests/baselines/reference/constDeclarations-access3.types b/tests/baselines/reference/constDeclarations-access3.types index 3e3bd07190..af8273ff0d 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 4b1902cf57..3cf15ca6a6 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/typeGuardNarrowsIndexedAccessOfKnownProperty.js b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js new file mode 100644 index 0000000000..7fa7a59abc --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.js @@ -0,0 +1,123 @@ +//// [typeGuardNarrowsIndexedAccessOfKnownProperty.ts] +interface Square { + ["dash-ok"]: "square"; + ["square-size"]: number; +} + interface Rectangle { + ["dash-ok"]: "rectangle"; + width: number; + height: number; +} + interface Circle { + ["dash-ok"]: "circle"; + radius: number; +} + type Shape = Square | Rectangle | Circle; +interface Subshape { + "0": { + sub: { + under: { + shape: Shape; + } + } + } +} +function area(s: Shape): number { + 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"]["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"]; + } +} + +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['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"]["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"]; + } +} +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 0000000000..3a8e434aa3 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.symbols @@ -0,0 +1,268 @@ +=== tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts === +interface Square { +>Square : Symbol(Square, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 0)) + + ["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)) + + ["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)) + + ["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, 29)) + + height: number; +>height : Symbol(Rectangle.height, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 6, 18)) +} + interface Circle { +>Circle : Symbol(Circle, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 8, 1)) + + ["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, 26)) +} + 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['dash-ok']) { +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) +>'dash-ok' : Symbol(["dash-ok"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 0, 18), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 4, 22), Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 9, 19)) + + case "square": return s['square-size'] * s['square-size']; +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) +>'square-size' : Symbol(Square["square-size"], Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 1, 26)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) +>'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, 29)) +>s : Symbol(s, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty.ts, 23, 14)) +>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)) + + 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, 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, 26)) + } +} +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"]["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)) +>"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["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)) +>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)) +>"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)) +>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)) +>"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)) +>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, 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)) +>"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, 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)) +>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, 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)) +>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, 26)) + } +} + +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 0000000000..4e3d2e0743 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty.types @@ -0,0 +1,305 @@ +=== tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts === +interface Square { + ["dash-ok"]: "square"; +>["dash-ok"] : "square" +>"dash-ok" : "dash-ok" + + ["square-size"]: number; +>["square-size"] : number +>"square-size" : "square-size" +} + interface Rectangle { + ["dash-ok"]: "rectangle"; +>["dash-ok"] : "rectangle" +>"dash-ok" : "dash-ok" + + width: number; +>width : number + + height: number; +>height : number +} + interface Circle { + ["dash-ok"]: "circle"; +>["dash-ok"] : "circle" +>"dash-ok" : "dash-ok" + + 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['dash-ok']) { +>s['dash-ok'] : "square" | "rectangle" | "circle" +>s : Shape +>'dash-ok' : "dash-ok" + + case "square": return s['square-size'] * s['square-size']; +>"square" : "square" +>s['square-size'] * s['square-size'] : number +>s['square-size'] : number +>s : Square +>'square-size' : "square-size" +>s['square-size'] : number +>s : Square +>'square-size' : "square-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"]["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; }; } +>s[0] : { sub: { under: { shape: Shape; }; }; } +>s : Subshape +>0 : 0 +>"sub" : "sub" +>under : { shape: Shape; } +>"shape" : "shape" +>"dash-ok" : "dash-ok" + + case "square": return s[0].sub.under.shape["square-size"] * s[0].sub.under.shape["square-size"]; +>"square" : "square" +>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; }; } +>s[0] : { sub: { under: { shape: Shape; }; }; } +>s : Subshape +>0 : 0 +>sub : { under: { shape: Shape; }; } +>under : { shape: Shape; } +>shape : Square +>"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; }; } +>s[0] : { sub: { under: { shape: Shape; }; }; } +>s : Subshape +>0 : 0 +>sub : { under: { shape: Shape; }; } +>under : { shape: Shape; } +>shape : Square +>"square-size" : "square-size" + + 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/baselines/reference/unionTypeWithIndexSignature.errors.txt b/tests/baselines/reference/unionTypeWithIndexSignature.errors.txt index e95a184d15..7f4675b037 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 417d62e706..a699d51391 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" diff --git a/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts b/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts new file mode 100644 index 0000000000..9d36f3c8ea --- /dev/null +++ b/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty.ts @@ -0,0 +1,80 @@ +// @strict: true +interface Square { + ["dash-ok"]: "square"; + ["square-size"]: number; +} + interface Rectangle { + ["dash-ok"]: "rectangle"; + width: number; + height: number; +} + interface Circle { + ["dash-ok"]: "circle"; + radius: number; +} + type Shape = Square | Rectangle | Circle; +interface Subshape { + "0": { + sub: { + under: { + shape: Shape; + } + } + } +} +function area(s: Shape): number { + 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"]["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"]; + } +} + +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'; +}