diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ac4f95611316a..b3a99be4155c1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22763,6 +22763,10 @@ namespace ts { getUnionType([extractDefinitelyFalsyTypes(strictNullChecks ? leftType : getBaseTypeOfLiteralType(rightType)), rightType]) : leftType; case SyntaxKind.BarBarToken: + leftType = leftType.flags & TypeFlags.IndexedAccess ? + any(getPropertiesOfType((<IndexedAccessType>leftType).objectType), prop => !!(prop.flags & SymbolFlags.Optional)) ? + neverType : leftType : + leftType; return getTypeFacts(leftType) & TypeFacts.Falsy ? getUnionType([removeDefinitelyFalsyTypes(leftType), rightType], UnionReduction.Subtype) : leftType; diff --git a/src/compiler/core.ts b/src/compiler/core.ts index a2f2b24550207..3d5897ae84474 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -232,6 +232,18 @@ namespace ts { return undefined; } + /** + * Returns true if any of the items in array satisfies predicate 'if provided' or is truthy + */ + export function any<T>(array: ReadonlyArray<T>, predicate?: (i: any) => boolean): boolean { + for (const item of array) { + if (!predicate ? item : predicate(item)) { + return true; + } + } + return false; + } + export function firstDefinedIterator<T, U>(iter: Iterator<T>, callback: (element: T) => U | undefined): U | undefined { while (true) { const { value, done } = iter.next(); diff --git a/tests/baselines/reference/shortCircuitEvaluation.js b/tests/baselines/reference/shortCircuitEvaluation.js new file mode 100644 index 0000000000000..3d0f0bf351020 --- /dev/null +++ b/tests/baselines/reference/shortCircuitEvaluation.js @@ -0,0 +1,28 @@ +//// [shortCircuitEvaluation.ts] +let a = undefined || 1; +a++ + +let b = null || 'foo'; +b.concat('bar') + +type EventType = 'click' | 'dblclick' + +const handlerMap: { [P in EventType]?: any[] } = {} + +function addHandler<P extends EventType>(evType: P) { + const handlerList = handlerMap[evType] || <any[]>[] + handlerList.push({}) + handlerMap[evType] = handlerList +} + +//// [shortCircuitEvaluation.js] +var a = undefined || 1; +a++; +var b = null || 'foo'; +b.concat('bar'); +var handlerMap = {}; +function addHandler(evType) { + var handlerList = handlerMap[evType] || []; + handlerList.push({}); + handlerMap[evType] = handlerList; +} diff --git a/tests/baselines/reference/shortCircuitEvaluation.symbols b/tests/baselines/reference/shortCircuitEvaluation.symbols new file mode 100644 index 0000000000000..123afe2520cf2 --- /dev/null +++ b/tests/baselines/reference/shortCircuitEvaluation.symbols @@ -0,0 +1,46 @@ +=== tests/cases/conformance/expressions/binaryOperators/logicalOrOperator/shortCircuitEvaluation.ts === +let a = undefined || 1; +>a : Symbol(a, Decl(shortCircuitEvaluation.ts, 0, 3)) +>undefined : Symbol(undefined) + +a++ +>a : Symbol(a, Decl(shortCircuitEvaluation.ts, 0, 3)) + +let b = null || 'foo'; +>b : Symbol(b, Decl(shortCircuitEvaluation.ts, 3, 3)) + +b.concat('bar') +>b.concat : Symbol(String.concat, Decl(lib.es5.d.ts, --, --)) +>b : Symbol(b, Decl(shortCircuitEvaluation.ts, 3, 3)) +>concat : Symbol(String.concat, Decl(lib.es5.d.ts, --, --)) + +type EventType = 'click' | 'dblclick' +>EventType : Symbol(EventType, Decl(shortCircuitEvaluation.ts, 4, 15)) + +const handlerMap: { [P in EventType]?: any[] } = {} +>handlerMap : Symbol(handlerMap, Decl(shortCircuitEvaluation.ts, 8, 5)) +>P : Symbol(P, Decl(shortCircuitEvaluation.ts, 8, 21)) +>EventType : Symbol(EventType, Decl(shortCircuitEvaluation.ts, 4, 15)) + +function addHandler<P extends EventType>(evType: P) { +>addHandler : Symbol(addHandler, Decl(shortCircuitEvaluation.ts, 8, 51)) +>P : Symbol(P, Decl(shortCircuitEvaluation.ts, 10, 20)) +>EventType : Symbol(EventType, Decl(shortCircuitEvaluation.ts, 4, 15)) +>evType : Symbol(evType, Decl(shortCircuitEvaluation.ts, 10, 41)) +>P : Symbol(P, Decl(shortCircuitEvaluation.ts, 10, 20)) + + const handlerList = handlerMap[evType] || <any[]>[] +>handlerList : Symbol(handlerList, Decl(shortCircuitEvaluation.ts, 11, 7)) +>handlerMap : Symbol(handlerMap, Decl(shortCircuitEvaluation.ts, 8, 5)) +>evType : Symbol(evType, Decl(shortCircuitEvaluation.ts, 10, 41)) + + handlerList.push({}) +>handlerList.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>handlerList : Symbol(handlerList, Decl(shortCircuitEvaluation.ts, 11, 7)) +>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) + + handlerMap[evType] = handlerList +>handlerMap : Symbol(handlerMap, Decl(shortCircuitEvaluation.ts, 8, 5)) +>evType : Symbol(evType, Decl(shortCircuitEvaluation.ts, 10, 41)) +>handlerList : Symbol(handlerList, Decl(shortCircuitEvaluation.ts, 11, 7)) +} diff --git a/tests/baselines/reference/shortCircuitEvaluation.types b/tests/baselines/reference/shortCircuitEvaluation.types new file mode 100644 index 0000000000000..9142a4708e208 --- /dev/null +++ b/tests/baselines/reference/shortCircuitEvaluation.types @@ -0,0 +1,58 @@ +=== tests/cases/conformance/expressions/binaryOperators/logicalOrOperator/shortCircuitEvaluation.ts === +let a = undefined || 1; +>a : number +>undefined || 1 : 1 +>undefined : undefined +>1 : 1 + +a++ +>a++ : number +>a : number + +let b = null || 'foo'; +>b : string +>null || 'foo' : "foo" +>null : null +>'foo' : "foo" + +b.concat('bar') +>b.concat('bar') : string +>b.concat : (...strings: string[]) => string +>b : string +>concat : (...strings: string[]) => string +>'bar' : "bar" + +type EventType = 'click' | 'dblclick' +>EventType : EventType + +const handlerMap: { [P in EventType]?: any[] } = {} +>handlerMap : { click?: any[] | undefined; dblclick?: any[] | undefined; } +>{} : {} + +function addHandler<P extends EventType>(evType: P) { +>addHandler : <P extends EventType>(evType: P) => void +>evType : P + + const handlerList = handlerMap[evType] || <any[]>[] +>handlerList : any[] +>handlerMap[evType] || <any[]>[] : any[] +>handlerMap[evType] : { click?: any[] | undefined; dblclick?: any[] | undefined; }[P] +>handlerMap : { click?: any[] | undefined; dblclick?: any[] | undefined; } +>evType : P +><any[]>[] : any[] +>[] : never[] + + handlerList.push({}) +>handlerList.push({}) : number +>handlerList.push : (...items: any[]) => number +>handlerList : any[] +>push : (...items: any[]) => number +>{} : {} + + handlerMap[evType] = handlerList +>handlerMap[evType] = handlerList : any[] +>handlerMap[evType] : { click?: any[] | undefined; dblclick?: any[] | undefined; }[P] +>handlerMap : { click?: any[] | undefined; dblclick?: any[] | undefined; } +>evType : P +>handlerList : any[] +} diff --git a/tests/cases/conformance/expressions/binaryOperators/logicalOrOperator/shortCircuitEvaluation.ts b/tests/cases/conformance/expressions/binaryOperators/logicalOrOperator/shortCircuitEvaluation.ts new file mode 100644 index 0000000000000..f2ee19e655bee --- /dev/null +++ b/tests/cases/conformance/expressions/binaryOperators/logicalOrOperator/shortCircuitEvaluation.ts @@ -0,0 +1,16 @@ +// @strictNullChecks: true +let a = undefined || 1; +a++ + +let b = null || 'foo'; +b.concat('bar') + +type EventType = 'click' | 'dblclick' + +const handlerMap: { [P in EventType]?: any[] } = {} + +function addHandler<P extends EventType>(evType: P) { + const handlerList = handlerMap[evType] || <any[]>[] + handlerList.push({}) + handlerMap[evType] = handlerList +} \ No newline at end of file