Fixed accidental propagation of caching-related objectFlags (#52546)

This commit is contained in:
Mateusz Burzyński
2023-02-01 13:18:19 -08:00
committed by GitHub
parent 3880fce249
commit c72d9292d9
4 changed files with 113 additions and 3 deletions
+5 -3
View File
@@ -16270,7 +16270,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
// This function assumes the constituent type list is sorted and deduplicated.
function getUnionTypeFromSortedList(types: Type[], objectFlags: ObjectFlags, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], origin?: Type): Type {
function getUnionTypeFromSortedList(types: Type[], precomputedObjectFlags: ObjectFlags, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], origin?: Type): Type {
if (types.length === 0) {
return neverType;
}
@@ -16285,7 +16285,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
let type = unionTypes.get(id);
if (!type) {
type = createType(TypeFlags.Union) as UnionType;
type.objectFlags = objectFlags | getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable);
type.objectFlags = precomputedObjectFlags | getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable);
type.types = types;
type.origin = origin;
type.aliasSymbol = aliasSymbol;
@@ -25649,7 +25649,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
newOrigin = createOriginUnionOrIntersectionType(TypeFlags.Union, originFiltered);
}
}
return getUnionTypeFromSortedList(filtered, (type as UnionType).objectFlags, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, newOrigin);
// filtering could remove intersections so `ContainsIntersections` might be forwarded "incorrectly"
// it is purely an optimization hint so there is no harm in accidentally forwarding it
return getUnionTypeFromSortedList(filtered, (type as UnionType).objectFlags & (ObjectFlags.PrimitiveUnion | ObjectFlags.ContainsIntersections), /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, newOrigin);
}
return type.flags & TypeFlags.Never || f(type) ? type : neverType;
}
@@ -0,0 +1,41 @@
=== tests/cases/compiler/unknownLikeUnionObjectFlagsNotPropagated.ts ===
// repro from #52475#issuecomment-1411215277
type MyType = {} | null | undefined;
>MyType : Symbol(MyType, Decl(unknownLikeUnionObjectFlagsNotPropagated.ts, 0, 0))
const myVar: MyType = null as MyType;
>myVar : Symbol(myVar, Decl(unknownLikeUnionObjectFlagsNotPropagated.ts, 4, 5))
>MyType : Symbol(MyType, Decl(unknownLikeUnionObjectFlagsNotPropagated.ts, 0, 0))
>MyType : Symbol(MyType, Decl(unknownLikeUnionObjectFlagsNotPropagated.ts, 0, 0))
myVar?.toLocaleString;
>myVar?.toLocaleString : Symbol(Object.toLocaleString, Decl(lib.es5.d.ts, --, --))
>myVar : Symbol(myVar, Decl(unknownLikeUnionObjectFlagsNotPropagated.ts, 4, 5))
>toLocaleString : Symbol(Object.toLocaleString, Decl(lib.es5.d.ts, --, --))
myVar;
>myVar : Symbol(myVar, Decl(unknownLikeUnionObjectFlagsNotPropagated.ts, 4, 5))
async function myUnusedFunction() {
>myUnusedFunction : Symbol(myUnusedFunction, Decl(unknownLikeUnionObjectFlagsNotPropagated.ts, 7, 6))
const fetch1 = Promise.resolve(['hello', 'world']);
>fetch1 : Symbol(fetch1, Decl(unknownLikeUnionObjectFlagsNotPropagated.ts, 10, 9))
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
const [data1] = await Promise.all([fetch1]);
>data1 : Symbol(data1, Decl(unknownLikeUnionObjectFlagsNotPropagated.ts, 11, 11))
>Promise.all : Symbol(PromiseConstructor.all, Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>all : Symbol(PromiseConstructor.all, Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
>fetch1 : Symbol(fetch1, Decl(unknownLikeUnionObjectFlagsNotPropagated.ts, 10, 9))
data1.length;
>data1.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>data1 : Symbol(data1, Decl(unknownLikeUnionObjectFlagsNotPropagated.ts, 11, 11))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
}
@@ -0,0 +1,49 @@
=== tests/cases/compiler/unknownLikeUnionObjectFlagsNotPropagated.ts ===
// repro from #52475#issuecomment-1411215277
type MyType = {} | null | undefined;
>MyType : {} | null | undefined
>null : null
const myVar: MyType = null as MyType;
>myVar : MyType
>null as MyType : MyType
>null : null
myVar?.toLocaleString;
>myVar?.toLocaleString : (() => string) | undefined
>myVar : MyType
>toLocaleString : (() => string) | undefined
myVar;
>myVar : MyType
async function myUnusedFunction() {
>myUnusedFunction : () => Promise<void>
const fetch1 = Promise.resolve(['hello', 'world']);
>fetch1 : Promise<string[]>
>Promise.resolve(['hello', 'world']) : Promise<string[]>
>Promise.resolve : { (): Promise<void>; <T>(value: T): Promise<Awaited<T>>; <T>(value: T | PromiseLike<T>): Promise<Awaited<T>>; }
>Promise : PromiseConstructor
>resolve : { (): Promise<void>; <T>(value: T): Promise<Awaited<T>>; <T>(value: T | PromiseLike<T>): Promise<Awaited<T>>; }
>['hello', 'world'] : string[]
>'hello' : "hello"
>'world' : "world"
const [data1] = await Promise.all([fetch1]);
>data1 : string[]
>await Promise.all([fetch1]) : [string[]]
>Promise.all([fetch1]) : Promise<[string[]]>
>Promise.all : { <T>(values: Iterable<T | PromiseLike<T>>): Promise<Awaited<T>[]>; <T extends readonly unknown[] | []>(values: T): Promise<{ -readonly [P in keyof T]: Awaited<T[P]>; }>; }
>Promise : PromiseConstructor
>all : { <T>(values: Iterable<T | PromiseLike<T>>): Promise<Awaited<T>[]>; <T extends readonly unknown[] | []>(values: T): Promise<{ -readonly [P in keyof T]: Awaited<T[P]>; }>; }
>[fetch1] : [Promise<string[]>]
>fetch1 : Promise<string[]>
data1.length;
>data1.length : number
>data1 : string[]
>length : number
}
@@ -0,0 +1,18 @@
// @strict: true
// @noEmit: true
// @lib: esnext
// repro from #52475#issuecomment-1411215277
type MyType = {} | null | undefined;
const myVar: MyType = null as MyType;
myVar?.toLocaleString;
myVar;
async function myUnusedFunction() {
const fetch1 = Promise.resolve(['hello', 'world']);
const [data1] = await Promise.all([fetch1]);
data1.length;
}