Avoid pulling object function property augmentations when resolving intersections' properties (#54753)

Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
This commit is contained in:
Mateusz Burzyński 2023-11-30 00:37:13 +01:00 committed by GitHub
parent 24166a4ed8
commit 0e610182d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 322 additions and 4 deletions

View File

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

View File

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

View File

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

View File

@ -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<typeof EmberObject> &
>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))

View File

@ -0,0 +1,34 @@
//// [tests/cases/compiler/intersectionWithConstructSignaturePrototypeResult.ts] ////
=== intersectionWithConstructSignaturePrototypeResult.ts ===
declare class EmberObject {}
>EmberObject : EmberObject
type PersonType = Readonly<typeof EmberObject> &
>PersonType : Readonly<typeof EmberObject> & (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

View File

@ -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<string[]>;
>TA1 : { getConcat: () => { (...items: ConcatArray<string>[]): string[]; (...items: (string | ConcatArray<string>)[]): 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: () => { <S extends string>(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: () => <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]; getFilter: () => { <S_1 extends string>(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; <U_1>(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; <U_2>(callbackfn: (previousValue: U_2, currentValue: string, currentIndex: number, array: string[]) => U_2, initialValue: U_2): U_2; }; }
>TA1 : { getConcat: () => { (...items: ConcatArray<string>[]): string[]; (...items: (string | ConcatArray<string>)[]): 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: () => { <S extends string>(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: () => <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]; getFilter: () => { <S_1 extends string>(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; <U_1>(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; <U_2>(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>[]): (number | boolean)[]; (...items: (number | boolean | ConcatArray<number | boolean>)[]): (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: () => { <S extends number | boolean>(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: () => <U>(callbackfn: (value: number | boolean, index: number, array: (number | boolean)[]) => U, thisArg?: any) => U[]; getFilter: () => { <S_1 extends number | boolean>(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; <U_1>(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; <U_2>(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>[]): (number | boolean)[]; (...items: (number | boolean | ConcatArray<number | boolean>)[]): (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: () => { <S extends number | boolean>(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: () => <U>(callbackfn: (value: number | boolean, index: number, array: (number | boolean)[]) => U, thisArg?: any) => U[]; getFilter: () => { <S_1 extends number | boolean>(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; <U_1>(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; <U_2>(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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,16 @@
// @strict: true
// @noEmit: true
declare class EmberObject {}
type PersonType = Readonly<typeof EmberObject> &
(new (properties?: object) => {
firstName: string;
lastName: string;
} & EmberObject) &
(new (...args: any[]) => {
firstName: string;
lastName: string;
} & EmberObject);
type PersonPrototype = PersonType["prototype"];

View File

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

View File

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