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
This commit is contained in:
Nathan Shively-Sanders 2018-08-15 09:58:39 -07:00 committed by GitHub
parent b9bd0d9a3f
commit 2bfd919b6a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 861 additions and 55 deletions

View file

@ -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(<CallExpression>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((<PropertyAccessExpression>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(<Identifier>node);
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
if (currentFlow && isNarrowableReference(<Expression>node)) {
node.flowNode = currentFlow;
}

View file

@ -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 &&
(<PropertyAccessExpression>source).name.escapedText === (<PropertyAccessExpression>target).name.escapedText &&
isMatchingReference((<PropertyAccessExpression>source).expression, (<PropertyAccessExpression>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 = (<PropertyAccessExpression>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 = (<FlowStart>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, <PropertyAccessExpression>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, (<PropertyAccessExpression>expr).expression) &&
isDiscriminantProperty(computedType, (<PropertyAccessExpression>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, <PropertyAccessExpression>expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy));
return narrowTypeByDiscriminant(type, <PropertyAccessExpression | ElementAccessExpression>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, <PropertyAccessExpression>left, t => narrowTypeByEquality(t, operator, right, assumeTrue));
return narrowTypeByDiscriminant(type, <PropertyAccessExpression | ElementAccessExpression>left, t => narrowTypeByEquality(t, operator, right, assumeTrue));
}
if (isMatchingReferenceDiscriminant(right, declaredType)) {
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>right, t => narrowTypeByEquality(t, operator, left, assumeTrue));
return narrowTypeByDiscriminant(type, <PropertyAccessExpression | ElementAccessExpression>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, <CallExpression>expr, assumeTrue);

View file

@ -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

View file

@ -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 ===

View file

@ -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;

View file

@ -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)
}

View file

@ -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"
}

View file

@ -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

View file

@ -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"

View file

@ -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';
}