diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 833428e3321db..07bbb846b466b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20855,6 +20855,8 @@ namespace ts { return isAccessExpression(target) && getAccessedPropertyName(source) === getAccessedPropertyName(target) && isMatchingReference((source).expression, target.expression); + case SyntaxKind.BinaryExpression: + return (isBinaryExpression(source) && source.operatorToken.kind === SyntaxKind.CommaToken && isMatchingReference(source.right, target)); } return false; } diff --git a/tests/baselines/reference/narrowCommaOperatorNestedWithinLHS.js b/tests/baselines/reference/narrowCommaOperatorNestedWithinLHS.js new file mode 100644 index 0000000000000..a3137b050c22b --- /dev/null +++ b/tests/baselines/reference/narrowCommaOperatorNestedWithinLHS.js @@ -0,0 +1,32 @@ +//// [narrowCommaOperatorNestedWithinLHS.ts] +const otherValue = () => true; +const value: { inner: number | string } = null as any; + +function isNumber(obj: any): obj is number { + return true; // method implementation irrelevant +} + +if (typeof (otherValue(), value).inner === 'number') { + const a = value.inner; // number + const b: number = (otherValue(), value).inner; // string | number , but should be number +} + +if (isNumber((otherValue(), value).inner)) { + const a = value.inner; // number + const b: number = (otherValue(), value).inner; // string | number , but should be number +} + +//// [narrowCommaOperatorNestedWithinLHS.js] +var otherValue = function () { return true; }; +var value = null; +function isNumber(obj) { + return true; // method implementation irrelevant +} +if (typeof (otherValue(), value).inner === 'number') { + var a = value.inner; // number + var b = (otherValue(), value).inner; // string | number , but should be number +} +if (isNumber((otherValue(), value).inner)) { + var a = value.inner; // number + var b = (otherValue(), value).inner; // string | number , but should be number +} diff --git a/tests/baselines/reference/narrowCommaOperatorNestedWithinLHS.symbols b/tests/baselines/reference/narrowCommaOperatorNestedWithinLHS.symbols new file mode 100644 index 0000000000000..c1f0a7b940188 --- /dev/null +++ b/tests/baselines/reference/narrowCommaOperatorNestedWithinLHS.symbols @@ -0,0 +1,56 @@ +=== tests/cases/compiler/narrowCommaOperatorNestedWithinLHS.ts === +const otherValue = () => true; +>otherValue : Symbol(otherValue, Decl(narrowCommaOperatorNestedWithinLHS.ts, 0, 5)) + +const value: { inner: number | string } = null as any; +>value : Symbol(value, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 5)) +>inner : Symbol(inner, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 14)) + +function isNumber(obj: any): obj is number { +>isNumber : Symbol(isNumber, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 54)) +>obj : Symbol(obj, Decl(narrowCommaOperatorNestedWithinLHS.ts, 3, 18)) +>obj : Symbol(obj, Decl(narrowCommaOperatorNestedWithinLHS.ts, 3, 18)) + + return true; // method implementation irrelevant +} + +if (typeof (otherValue(), value).inner === 'number') { +>(otherValue(), value).inner : Symbol(inner, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 14)) +>otherValue : Symbol(otherValue, Decl(narrowCommaOperatorNestedWithinLHS.ts, 0, 5)) +>value : Symbol(value, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 5)) +>inner : Symbol(inner, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 14)) + + const a = value.inner; // number +>a : Symbol(a, Decl(narrowCommaOperatorNestedWithinLHS.ts, 8, 9)) +>value.inner : Symbol(inner, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 14)) +>value : Symbol(value, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 5)) +>inner : Symbol(inner, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 14)) + + const b: number = (otherValue(), value).inner; // string | number , but should be number +>b : Symbol(b, Decl(narrowCommaOperatorNestedWithinLHS.ts, 9, 9)) +>(otherValue(), value).inner : Symbol(inner, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 14)) +>otherValue : Symbol(otherValue, Decl(narrowCommaOperatorNestedWithinLHS.ts, 0, 5)) +>value : Symbol(value, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 5)) +>inner : Symbol(inner, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 14)) +} + +if (isNumber((otherValue(), value).inner)) { +>isNumber : Symbol(isNumber, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 54)) +>(otherValue(), value).inner : Symbol(inner, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 14)) +>otherValue : Symbol(otherValue, Decl(narrowCommaOperatorNestedWithinLHS.ts, 0, 5)) +>value : Symbol(value, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 5)) +>inner : Symbol(inner, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 14)) + + const a = value.inner; // number +>a : Symbol(a, Decl(narrowCommaOperatorNestedWithinLHS.ts, 13, 9)) +>value.inner : Symbol(inner, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 14)) +>value : Symbol(value, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 5)) +>inner : Symbol(inner, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 14)) + + const b: number = (otherValue(), value).inner; // string | number , but should be number +>b : Symbol(b, Decl(narrowCommaOperatorNestedWithinLHS.ts, 14, 9)) +>(otherValue(), value).inner : Symbol(inner, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 14)) +>otherValue : Symbol(otherValue, Decl(narrowCommaOperatorNestedWithinLHS.ts, 0, 5)) +>value : Symbol(value, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 5)) +>inner : Symbol(inner, Decl(narrowCommaOperatorNestedWithinLHS.ts, 1, 14)) +} diff --git a/tests/baselines/reference/narrowCommaOperatorNestedWithinLHS.types b/tests/baselines/reference/narrowCommaOperatorNestedWithinLHS.types new file mode 100644 index 0000000000000..7ad1879002587 --- /dev/null +++ b/tests/baselines/reference/narrowCommaOperatorNestedWithinLHS.types @@ -0,0 +1,76 @@ +=== tests/cases/compiler/narrowCommaOperatorNestedWithinLHS.ts === +const otherValue = () => true; +>otherValue : () => boolean +>() => true : () => boolean +>true : true + +const value: { inner: number | string } = null as any; +>value : { inner: number | string; } +>inner : string | number +>null as any : any +>null : null + +function isNumber(obj: any): obj is number { +>isNumber : (obj: any) => obj is number +>obj : any + + return true; // method implementation irrelevant +>true : true +} + +if (typeof (otherValue(), value).inner === 'number') { +>typeof (otherValue(), value).inner === 'number' : boolean +>typeof (otherValue(), value).inner : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>(otherValue(), value).inner : string | number +>(otherValue(), value) : { inner: string | number; } +>otherValue(), value : { inner: string | number; } +>otherValue() : boolean +>otherValue : () => boolean +>value : { inner: string | number; } +>inner : string | number +>'number' : "number" + + const a = value.inner; // number +>a : number +>value.inner : number +>value : { inner: string | number; } +>inner : number + + const b: number = (otherValue(), value).inner; // string | number , but should be number +>b : number +>(otherValue(), value).inner : number +>(otherValue(), value) : { inner: string | number; } +>otherValue(), value : { inner: string | number; } +>otherValue() : boolean +>otherValue : () => boolean +>value : { inner: string | number; } +>inner : number +} + +if (isNumber((otherValue(), value).inner)) { +>isNumber((otherValue(), value).inner) : boolean +>isNumber : (obj: any) => obj is number +>(otherValue(), value).inner : string | number +>(otherValue(), value) : { inner: string | number; } +>otherValue(), value : { inner: string | number; } +>otherValue() : boolean +>otherValue : () => boolean +>value : { inner: string | number; } +>inner : string | number + + const a = value.inner; // number +>a : number +>value.inner : number +>value : { inner: string | number; } +>inner : number + + const b: number = (otherValue(), value).inner; // string | number , but should be number +>b : number +>(otherValue(), value).inner : number +>(otherValue(), value) : { inner: string | number; } +>otherValue(), value : { inner: string | number; } +>otherValue() : boolean +>otherValue : () => boolean +>value : { inner: string | number; } +>inner : number +} diff --git a/tests/cases/compiler/narrowCommaOperatorNestedWithinLHS.ts b/tests/cases/compiler/narrowCommaOperatorNestedWithinLHS.ts new file mode 100644 index 0000000000000..53e1891349e27 --- /dev/null +++ b/tests/cases/compiler/narrowCommaOperatorNestedWithinLHS.ts @@ -0,0 +1,16 @@ +const otherValue = () => true; +const value: { inner: number | string } = null as any; + +function isNumber(obj: any): obj is number { + return true; // method implementation irrelevant +} + +if (typeof (otherValue(), value).inner === 'number') { + const a = value.inner; // number + const b: number = (otherValue(), value).inner; // string | number , but should be number +} + +if (isNumber((otherValue(), value).inner)) { + const a = value.inner; // number + const b: number = (otherValue(), value).inner; // string | number , but should be number +} \ No newline at end of file