diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 189d82eeab9..90d453bcd99 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13980,7 +13980,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { for (const current of type.types) { for (const prop of getPropertiesOfType(current)) { if (!members.has(prop.escapedName)) { - const combinedProp = getPropertyOfUnionOrIntersectionType(type, prop.escapedName); + const combinedProp = getPropertyOfUnionOrIntersectionType(type, prop.escapedName, /*skipObjectFunctionPropertyAugment*/ !!(type.flags & TypeFlags.Intersection)); if (combinedProp) { members.set(prop.escapedName, combinedProp); } @@ -14629,6 +14629,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { type.propertyCacheWithoutObjectFunctionPropertyAugment ||= createSymbolTable() : type.propertyCache ||= createSymbolTable(); properties.set(name, property); + if (skipObjectFunctionPropertyAugment && !type.propertyCache?.get(name)) { + const properties = type.propertyCache ||= createSymbolTable(); + properties.set(name, property); + } } } return property; @@ -14771,7 +14775,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } return getPropertyOfObjectType(globalObjectType, name); } - if (type.flags & TypeFlags.UnionOrIntersection) { + if (type.flags & TypeFlags.Intersection) { + const prop = getPropertyOfUnionOrIntersectionType(type as UnionOrIntersectionType, name, /*skipObjectFunctionPropertyAugment*/ true); + if (prop) { + return prop; + } + if (!skipObjectFunctionPropertyAugment) { + return getPropertyOfUnionOrIntersectionType(type as UnionOrIntersectionType, name, skipObjectFunctionPropertyAugment); + } + return undefined; + } + if (type.flags & TypeFlags.Union) { return getPropertyOfUnionOrIntersectionType(type as UnionOrIntersectionType, name, skipObjectFunctionPropertyAugment); } return undefined; diff --git a/tests/baselines/reference/intersectionIncludingPropFromGlobalAugmentation.symbols b/tests/baselines/reference/intersectionIncludingPropFromGlobalAugmentation.symbols new file mode 100644 index 00000000000..034306a4d3a --- /dev/null +++ b/tests/baselines/reference/intersectionIncludingPropFromGlobalAugmentation.symbols @@ -0,0 +1,37 @@ +//// [tests/cases/conformance/types/typeRelationships/assignmentCompatibility/intersectionIncludingPropFromGlobalAugmentation.ts] //// + +=== intersectionIncludingPropFromGlobalAugmentation.ts === +// repro from https://github.com/microsoft/TypeScript/issues/54345 + +interface Test1 { toString: null | 'string'; } +>Test1 : Symbol(Test1, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 0, 0)) +>toString : Symbol(Test1.toString, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 2, 17)) + +type Test2 = Test1 & { optional?: unknown }; +>Test2 : Symbol(Test2, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 2, 46)) +>Test1 : Symbol(Test1, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 0, 0)) +>optional : Symbol(optional, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 3, 22)) + +declare const source: Test1; +>source : Symbol(source, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 4, 13)) +>Test1 : Symbol(Test1, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 0, 0)) + +const target: Test2 = { ...source }; +>target : Symbol(target, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 5, 5)) +>Test2 : Symbol(Test2, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 2, 46)) +>source : Symbol(source, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 4, 13)) + +const toString = target.toString; +>toString : Symbol(toString, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 7, 5)) +>target.toString : Symbol(Test1.toString, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 2, 17)) +>target : Symbol(target, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 5, 5)) +>toString : Symbol(Test1.toString, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 2, 17)) + +const hasOwn = target.hasOwnProperty; // not an own member but it should still be accessible +>hasOwn : Symbol(hasOwn, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 8, 5)) +>target.hasOwnProperty : Symbol(Object.hasOwnProperty, Decl(lib.es5.d.ts, --, --)) +>target : Symbol(target, Decl(intersectionIncludingPropFromGlobalAugmentation.ts, 5, 5)) +>hasOwnProperty : Symbol(Object.hasOwnProperty, Decl(lib.es5.d.ts, --, --)) + +export {} + diff --git a/tests/baselines/reference/intersectionIncludingPropFromGlobalAugmentation.types b/tests/baselines/reference/intersectionIncludingPropFromGlobalAugmentation.types new file mode 100644 index 00000000000..8133c1d8985 --- /dev/null +++ b/tests/baselines/reference/intersectionIncludingPropFromGlobalAugmentation.types @@ -0,0 +1,34 @@ +//// [tests/cases/conformance/types/typeRelationships/assignmentCompatibility/intersectionIncludingPropFromGlobalAugmentation.ts] //// + +=== intersectionIncludingPropFromGlobalAugmentation.ts === +// repro from https://github.com/microsoft/TypeScript/issues/54345 + +interface Test1 { toString: null | 'string'; } +>toString : "string" | null + +type Test2 = Test1 & { optional?: unknown }; +>Test2 : Test1 & { optional?: unknown; } +>optional : unknown + +declare const source: Test1; +>source : Test1 + +const target: Test2 = { ...source }; +>target : Test2 +>{ ...source } : { toString: "string" | null; } +>source : Test1 + +const toString = target.toString; +>toString : "string" | null +>target.toString : "string" | null +>target : Test2 +>toString : "string" | null + +const hasOwn = target.hasOwnProperty; // not an own member but it should still be accessible +>hasOwn : (v: PropertyKey) => boolean +>target.hasOwnProperty : (v: PropertyKey) => boolean +>target : Test2 +>hasOwnProperty : (v: PropertyKey) => boolean + +export {} + diff --git a/tests/baselines/reference/intersectionWithConstructSignaturePrototypeResult.symbols b/tests/baselines/reference/intersectionWithConstructSignaturePrototypeResult.symbols new file mode 100644 index 00000000000..a7673bd8bcd --- /dev/null +++ b/tests/baselines/reference/intersectionWithConstructSignaturePrototypeResult.symbols @@ -0,0 +1,39 @@ +//// [tests/cases/compiler/intersectionWithConstructSignaturePrototypeResult.ts] //// + +=== intersectionWithConstructSignaturePrototypeResult.ts === +declare class EmberObject {} +>EmberObject : Symbol(EmberObject, Decl(intersectionWithConstructSignaturePrototypeResult.ts, 0, 0)) + +type PersonType = Readonly & +>PersonType : Symbol(PersonType, Decl(intersectionWithConstructSignaturePrototypeResult.ts, 0, 28)) +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) +>EmberObject : Symbol(EmberObject, Decl(intersectionWithConstructSignaturePrototypeResult.ts, 0, 0)) + + (new (properties?: object) => { +>properties : Symbol(properties, Decl(intersectionWithConstructSignaturePrototypeResult.ts, 3, 8)) + + firstName: string; +>firstName : Symbol(firstName, Decl(intersectionWithConstructSignaturePrototypeResult.ts, 3, 33)) + + lastName: string; +>lastName : Symbol(lastName, Decl(intersectionWithConstructSignaturePrototypeResult.ts, 4, 22)) + + } & EmberObject) & +>EmberObject : Symbol(EmberObject, Decl(intersectionWithConstructSignaturePrototypeResult.ts, 0, 0)) + + (new (...args: any[]) => { +>args : Symbol(args, Decl(intersectionWithConstructSignaturePrototypeResult.ts, 7, 8)) + + firstName: string; +>firstName : Symbol(firstName, Decl(intersectionWithConstructSignaturePrototypeResult.ts, 7, 28)) + + lastName: string; +>lastName : Symbol(lastName, Decl(intersectionWithConstructSignaturePrototypeResult.ts, 8, 22)) + + } & EmberObject); +>EmberObject : Symbol(EmberObject, Decl(intersectionWithConstructSignaturePrototypeResult.ts, 0, 0)) + +type PersonPrototype = PersonType["prototype"]; +>PersonPrototype : Symbol(PersonPrototype, Decl(intersectionWithConstructSignaturePrototypeResult.ts, 10, 19)) +>PersonType : Symbol(PersonType, Decl(intersectionWithConstructSignaturePrototypeResult.ts, 0, 28)) + diff --git a/tests/baselines/reference/intersectionWithConstructSignaturePrototypeResult.types b/tests/baselines/reference/intersectionWithConstructSignaturePrototypeResult.types new file mode 100644 index 00000000000..1284561880a --- /dev/null +++ b/tests/baselines/reference/intersectionWithConstructSignaturePrototypeResult.types @@ -0,0 +1,34 @@ +//// [tests/cases/compiler/intersectionWithConstructSignaturePrototypeResult.ts] //// + +=== intersectionWithConstructSignaturePrototypeResult.ts === +declare class EmberObject {} +>EmberObject : EmberObject + +type PersonType = Readonly & +>PersonType : Readonly & (new (properties?: object) => { firstName: string; lastName: string;} & EmberObject) & (new (...args: any[]) => { firstName: string; lastName: string;} & EmberObject) +>EmberObject : typeof EmberObject + + (new (properties?: object) => { +>properties : object | undefined + + firstName: string; +>firstName : string + + lastName: string; +>lastName : string + + } & EmberObject) & + (new (...args: any[]) => { +>args : any[] + + firstName: string; +>firstName : string + + lastName: string; +>lastName : string + + } & EmberObject); + +type PersonPrototype = PersonType["prototype"]; +>PersonPrototype : EmberObject + diff --git a/tests/baselines/reference/mappedTypeAsClauses.types b/tests/baselines/reference/mappedTypeAsClauses.types index 00323af7598..f9d426c0e8f 100644 --- a/tests/baselines/reference/mappedTypeAsClauses.types +++ b/tests/baselines/reference/mappedTypeAsClauses.types @@ -35,10 +35,10 @@ type TP1 = TypeFromDefs<{ name: 'a', type: string } | { name: 'b', type: number // No array or tuple type mapping when 'as N' clause present type TA1 = Getters; ->TA1 : { getConcat: () => { (...items: ConcatArray[]): string[]; (...items: (string | ConcatArray)[]): string[]; }; getIndexOf: () => (searchElement: string, fromIndex?: number | undefined) => number; getLastIndexOf: () => (searchElement: string, fromIndex?: number | undefined) => number; getSlice: () => (start?: number | undefined, end?: number | undefined) => string[]; getLength: () => number; getToString: () => () => string; getToLocaleString: () => () => string; getPop: () => () => string | undefined; getPush: () => (...items: string[]) => number; getJoin: () => (separator?: string | undefined) => string; getReverse: () => () => string[]; getShift: () => () => string | undefined; getSort: () => (compareFn?: ((a: string, b: string) => number) | undefined) => string[]; getSplice: () => { (start: number, deleteCount?: number | undefined): string[]; (start: number, deleteCount: number, ...items: string[]): string[]; }; getUnshift: () => (...items: string[]) => number; getEvery: () => { (predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): boolean; }; getSome: () => (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any) => boolean; getForEach: () => (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void; getMap: () => (callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]; getFilter: () => { (predicate: (value: string, index: number, array: string[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): string[]; }; getReduce: () => { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U_1, currentValue: string, currentIndex: number, array: string[]) => U_1, initialValue: U_1): U_1; }; getReduceRight: () => { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U_2, currentValue: string, currentIndex: number, array: string[]) => U_2, initialValue: U_2): U_2; }; } +>TA1 : { getConcat: () => { (...items: ConcatArray[]): string[]; (...items: (string | ConcatArray)[]): string[]; }; getIndexOf: () => (searchElement: string, fromIndex?: number | undefined) => number; getLastIndexOf: () => (searchElement: string, fromIndex?: number | undefined) => number; getSlice: () => (start?: number | undefined, end?: number | undefined) => string[]; getLength: () => number; getToLocaleString: () => () => string; getToString: () => () => string; getPop: () => () => string | undefined; getPush: () => (...items: string[]) => number; getJoin: () => (separator?: string | undefined) => string; getReverse: () => () => string[]; getShift: () => () => string | undefined; getSort: () => (compareFn?: ((a: string, b: string) => number) | undefined) => string[]; getSplice: () => { (start: number, deleteCount?: number | undefined): string[]; (start: number, deleteCount: number, ...items: string[]): string[]; }; getUnshift: () => (...items: string[]) => number; getEvery: () => { (predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): boolean; }; getSome: () => (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any) => boolean; getForEach: () => (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void; getMap: () => (callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]; getFilter: () => { (predicate: (value: string, index: number, array: string[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): string[]; }; getReduce: () => { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U_1, currentValue: string, currentIndex: number, array: string[]) => U_1, initialValue: U_1): U_1; }; getReduceRight: () => { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U_2, currentValue: string, currentIndex: number, array: string[]) => U_2, initialValue: U_2): U_2; }; } type TA2 = Getters<[number, boolean]>; ->TA2 : { getConcat: () => { (...items: ConcatArray[]): (number | boolean)[]; (...items: (number | boolean | ConcatArray)[]): (number | boolean)[]; }; getIndexOf: () => (searchElement: number | boolean, fromIndex?: number | undefined) => number; getLastIndexOf: () => (searchElement: number | boolean, fromIndex?: number | undefined) => number; getSlice: () => (start?: number | undefined, end?: number | undefined) => (number | boolean)[]; getLength: () => 2; getToString: () => () => string; getToLocaleString: () => () => string; getPop: () => () => number | boolean | undefined; getPush: () => (...items: (number | boolean)[]) => number; getJoin: () => (separator?: string | undefined) => string; getReverse: () => () => (number | boolean)[]; getShift: () => () => number | boolean | undefined; getSort: () => (compareFn?: ((a: number | boolean, b: number | boolean) => number) | undefined) => [number, boolean]; getSplice: () => { (start: number, deleteCount?: number | undefined): (number | boolean)[]; (start: number, deleteCount: number, ...items: (number | boolean)[]): (number | boolean)[]; }; getUnshift: () => (...items: (number | boolean)[]) => number; getEvery: () => { (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => unknown, thisArg?: any): boolean; }; getSome: () => (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => unknown, thisArg?: any) => boolean; getForEach: () => (callbackfn: (value: number | boolean, index: number, array: (number | boolean)[]) => void, thisArg?: any) => void; getMap: () => (callbackfn: (value: number | boolean, index: number, array: (number | boolean)[]) => U, thisArg?: any) => U[]; getFilter: () => { (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => unknown, thisArg?: any): (number | boolean)[]; }; getReduce: () => { (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean): number | boolean; (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean, initialValue: number | boolean): number | boolean; (callbackfn: (previousValue: U_1, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => U_1, initialValue: U_1): U_1; }; getReduceRight: () => { (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean): number | boolean; (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean, initialValue: number | boolean): number | boolean; (callbackfn: (previousValue: U_2, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => U_2, initialValue: U_2): U_2; }; get0: () => number; get1: () => boolean; } +>TA2 : { getConcat: () => { (...items: ConcatArray[]): (number | boolean)[]; (...items: (number | boolean | ConcatArray)[]): (number | boolean)[]; }; getIndexOf: () => (searchElement: number | boolean, fromIndex?: number | undefined) => number; getLastIndexOf: () => (searchElement: number | boolean, fromIndex?: number | undefined) => number; getSlice: () => (start?: number | undefined, end?: number | undefined) => (number | boolean)[]; getLength: () => 2; getToLocaleString: () => () => string; getToString: () => () => string; getPop: () => () => number | boolean | undefined; getPush: () => (...items: (number | boolean)[]) => number; getJoin: () => (separator?: string | undefined) => string; getReverse: () => () => (number | boolean)[]; getShift: () => () => number | boolean | undefined; getSort: () => (compareFn?: ((a: number | boolean, b: number | boolean) => number) | undefined) => [number, boolean]; getSplice: () => { (start: number, deleteCount?: number | undefined): (number | boolean)[]; (start: number, deleteCount: number, ...items: (number | boolean)[]): (number | boolean)[]; }; getUnshift: () => (...items: (number | boolean)[]) => number; getEvery: () => { (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => unknown, thisArg?: any): boolean; }; getSome: () => (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => unknown, thisArg?: any) => boolean; getForEach: () => (callbackfn: (value: number | boolean, index: number, array: (number | boolean)[]) => void, thisArg?: any) => void; getMap: () => (callbackfn: (value: number | boolean, index: number, array: (number | boolean)[]) => U, thisArg?: any) => U[]; getFilter: () => { (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => unknown, thisArg?: any): (number | boolean)[]; }; getReduce: () => { (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean): number | boolean; (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean, initialValue: number | boolean): number | boolean; (callbackfn: (previousValue: U_1, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => U_1, initialValue: U_1): U_1; }; getReduceRight: () => { (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean): number | boolean; (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean, initialValue: number | boolean): number | boolean; (callbackfn: (previousValue: U_2, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => U_2, initialValue: U_2): U_2; }; get0: () => number; get1: () => boolean; } // Filtering using 'as N' clause diff --git a/tests/baselines/reference/uncalledFunctionChecksInConditional2.errors.txt b/tests/baselines/reference/uncalledFunctionChecksInConditional2.errors.txt index af6aea3676d..54feef1029a 100644 --- a/tests/baselines/reference/uncalledFunctionChecksInConditional2.errors.txt +++ b/tests/baselines/reference/uncalledFunctionChecksInConditional2.errors.txt @@ -69,4 +69,16 @@ uncalledFunctionChecksInConditional2.ts(49,5): error TS2774: This condition will } } }; + + let _isMobile: boolean; + function isMobile() { + if (_isMobile === undefined) { + const isMobileMatch = + typeof window !== 'undefined' && + window.matchMedia && // no error + window.matchMedia('(max-device-width: 680px)'); + _isMobile = isMobileMatch && isMobileMatch.matches; + } + return _isMobile; + } \ No newline at end of file diff --git a/tests/baselines/reference/uncalledFunctionChecksInConditional2.js b/tests/baselines/reference/uncalledFunctionChecksInConditional2.js index cf63e988127..becc23beb1e 100644 --- a/tests/baselines/reference/uncalledFunctionChecksInConditional2.js +++ b/tests/baselines/reference/uncalledFunctionChecksInConditional2.js @@ -60,6 +60,18 @@ declare let inBrowser: boolean; } } }; + +let _isMobile: boolean; +function isMobile() { + if (_isMobile === undefined) { + const isMobileMatch = + typeof window !== 'undefined' && + window.matchMedia && // no error + window.matchMedia('(max-device-width: 680px)'); + _isMobile = isMobileMatch && isMobileMatch.matches; + } + return _isMobile; +} //// [uncalledFunctionChecksInConditional2.js] @@ -109,3 +121,13 @@ var _a; } } ; +var _isMobile; +function isMobile() { + if (_isMobile === undefined) { + var isMobileMatch = typeof window !== 'undefined' && + window.matchMedia && // no error + window.matchMedia('(max-device-width: 680px)'); + _isMobile = isMobileMatch && isMobileMatch.matches; + } + return _isMobile; +} diff --git a/tests/baselines/reference/uncalledFunctionChecksInConditional2.symbols b/tests/baselines/reference/uncalledFunctionChecksInConditional2.symbols index 19c500ea8be..a98be45df9f 100644 --- a/tests/baselines/reference/uncalledFunctionChecksInConditional2.symbols +++ b/tests/baselines/reference/uncalledFunctionChecksInConditional2.symbols @@ -174,3 +174,40 @@ declare let inBrowser: boolean; } }; +let _isMobile: boolean; +>_isMobile : Symbol(_isMobile, Decl(uncalledFunctionChecksInConditional2.ts, 60, 3)) + +function isMobile() { +>isMobile : Symbol(isMobile, Decl(uncalledFunctionChecksInConditional2.ts, 60, 23)) + + if (_isMobile === undefined) { +>_isMobile : Symbol(_isMobile, Decl(uncalledFunctionChecksInConditional2.ts, 60, 3)) +>undefined : Symbol(undefined) + + const isMobileMatch = +>isMobileMatch : Symbol(isMobileMatch, Decl(uncalledFunctionChecksInConditional2.ts, 63, 9)) + + typeof window !== 'undefined' && +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) + + window.matchMedia && // no error +>window.matchMedia : Symbol(matchMedia, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) +>matchMedia : Symbol(matchMedia, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + + window.matchMedia('(max-device-width: 680px)'); +>window.matchMedia : Symbol(matchMedia, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) +>matchMedia : Symbol(matchMedia, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + + _isMobile = isMobileMatch && isMobileMatch.matches; +>_isMobile : Symbol(_isMobile, Decl(uncalledFunctionChecksInConditional2.ts, 60, 3)) +>isMobileMatch : Symbol(isMobileMatch, Decl(uncalledFunctionChecksInConditional2.ts, 63, 9)) +>isMobileMatch.matches : Symbol(MediaQueryList.matches, Decl(lib.dom.d.ts, --, --)) +>isMobileMatch : Symbol(isMobileMatch, Decl(uncalledFunctionChecksInConditional2.ts, 63, 9)) +>matches : Symbol(MediaQueryList.matches, Decl(lib.dom.d.ts, --, --)) + } + return _isMobile; +>_isMobile : Symbol(_isMobile, Decl(uncalledFunctionChecksInConditional2.ts, 60, 3)) +} + diff --git a/tests/baselines/reference/uncalledFunctionChecksInConditional2.types b/tests/baselines/reference/uncalledFunctionChecksInConditional2.types index 60eeef5fb8d..a14c167b9aa 100644 --- a/tests/baselines/reference/uncalledFunctionChecksInConditional2.types +++ b/tests/baselines/reference/uncalledFunctionChecksInConditional2.types @@ -215,3 +215,50 @@ declare let inBrowser: boolean; } }; +let _isMobile: boolean; +>_isMobile : boolean + +function isMobile() { +>isMobile : () => boolean + + if (_isMobile === undefined) { +>_isMobile === undefined : boolean +>_isMobile : boolean +>undefined : undefined + + const isMobileMatch = +>isMobileMatch : false | MediaQueryList + + typeof window !== 'undefined' && +>typeof window !== 'undefined' && window.matchMedia && // no error window.matchMedia('(max-device-width: 680px)') : false | MediaQueryList +>typeof window !== 'undefined' && window.matchMedia : false | (((query: string) => MediaQueryList) & ((query: string) => MediaQueryList)) +>typeof window !== 'undefined' : boolean +>typeof window : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>window : Window & typeof globalThis +>'undefined' : "undefined" + + window.matchMedia && // no error +>window.matchMedia : ((query: string) => MediaQueryList) & ((query: string) => MediaQueryList) +>window : Window & typeof globalThis +>matchMedia : ((query: string) => MediaQueryList) & ((query: string) => MediaQueryList) + + window.matchMedia('(max-device-width: 680px)'); +>window.matchMedia('(max-device-width: 680px)') : MediaQueryList +>window.matchMedia : ((query: string) => MediaQueryList) & ((query: string) => MediaQueryList) +>window : Window & typeof globalThis +>matchMedia : ((query: string) => MediaQueryList) & ((query: string) => MediaQueryList) +>'(max-device-width: 680px)' : "(max-device-width: 680px)" + + _isMobile = isMobileMatch && isMobileMatch.matches; +>_isMobile = isMobileMatch && isMobileMatch.matches : boolean +>_isMobile : boolean +>isMobileMatch && isMobileMatch.matches : boolean +>isMobileMatch : false | MediaQueryList +>isMobileMatch.matches : boolean +>isMobileMatch : MediaQueryList +>matches : boolean + } + return _isMobile; +>_isMobile : boolean +} + diff --git a/tests/cases/compiler/intersectionWithConstructSignaturePrototypeResult.ts b/tests/cases/compiler/intersectionWithConstructSignaturePrototypeResult.ts new file mode 100644 index 00000000000..28eb6c6b956 --- /dev/null +++ b/tests/cases/compiler/intersectionWithConstructSignaturePrototypeResult.ts @@ -0,0 +1,16 @@ +// @strict: true +// @noEmit: true + +declare class EmberObject {} + +type PersonType = Readonly & + (new (properties?: object) => { + firstName: string; + lastName: string; + } & EmberObject) & + (new (...args: any[]) => { + firstName: string; + lastName: string; + } & EmberObject); + +type PersonPrototype = PersonType["prototype"]; diff --git a/tests/cases/compiler/uncalledFunctionChecksInConditional2.ts b/tests/cases/compiler/uncalledFunctionChecksInConditional2.ts index dc75f72cec4..425584ba171 100644 --- a/tests/cases/compiler/uncalledFunctionChecksInConditional2.ts +++ b/tests/cases/compiler/uncalledFunctionChecksInConditional2.ts @@ -59,3 +59,15 @@ declare let inBrowser: boolean; } } }; + +let _isMobile: boolean; +function isMobile() { + if (_isMobile === undefined) { + const isMobileMatch = + typeof window !== 'undefined' && + window.matchMedia && // no error + window.matchMedia('(max-device-width: 680px)'); + _isMobile = isMobileMatch && isMobileMatch.matches; + } + return _isMobile; +} diff --git a/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/intersectionIncludingPropFromGlobalAugmentation.ts b/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/intersectionIncludingPropFromGlobalAugmentation.ts new file mode 100644 index 00000000000..00a17068d9b --- /dev/null +++ b/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/intersectionIncludingPropFromGlobalAugmentation.ts @@ -0,0 +1,14 @@ +// @strict: true +// @noEmit: true + +// repro from https://github.com/microsoft/TypeScript/issues/54345 + +interface Test1 { toString: null | 'string'; } +type Test2 = Test1 & { optional?: unknown }; +declare const source: Test1; +const target: Test2 = { ...source }; + +const toString = target.toString; +const hasOwn = target.hasOwnProperty; // not an own member but it should still be accessible + +export {}