From 5625f8bff7c9ad556e26b326334894f3aa6e5ec6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 2 May 2024 08:56:36 -0700 Subject: [PATCH 1/4] Add IntersectionFlags --- src/compiler/checker.ts | 31 ++++++++++++++++--------------- src/compiler/types.ts | 7 +++++++ 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 53e9c4387974c..8a79207820984 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1104,6 +1104,7 @@ import { WideningContext, WithStatement, YieldExpression, + IntersectionFlags, } from "./_namespaces/ts"; import * as moduleSpecifiers from "./_namespaces/ts.moduleSpecifiers"; import * as performance from "./_namespaces/ts.performance"; @@ -14204,7 +14205,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } // The source types were normalized; ensure the result is normalized too. - return getNormalizedType(getIntersectionType(constraints), /*writing*/ false); + return getNormalizedType(getIntersectionType(constraints, IntersectionFlags.NoConstraintReduction), /*writing*/ false); } return undefined; } @@ -17467,7 +17468,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // a type alias of the form "type List = T & { next: List }" cannot be reduced during its declaration. // Also, unlike union types, the order of the constituent types is preserved in order that overload resolution // for intersections of types with signatures can be deterministic. - function getIntersectionType(types: readonly Type[], aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], noSupertypeReduction?: boolean): Type { + function getIntersectionType(types: readonly Type[], flags = IntersectionFlags.None, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type { const typeMembershipMap = new Map(); const includes = addTypesToIntersection(typeMembershipMap, 0 as TypeFlags, types); const typeSet: Type[] = arrayFrom(typeMembershipMap.values()); @@ -17512,7 +17513,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { includes & TypeFlags.Void && includes & TypeFlags.Undefined || includes & TypeFlags.IncludesEmptyObject && includes & TypeFlags.DefinitelyNonNullable ) { - if (!noSupertypeReduction) removeRedundantSupertypes(typeSet, includes); + if (!(flags & IntersectionFlags.NoSupertypeReduction)) removeRedundantSupertypes(typeSet, includes); } if (includes & TypeFlags.IncludesMissingType) { typeSet[typeSet.indexOf(undefinedType)] = missingType; @@ -17523,7 +17524,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (typeSet.length === 1) { return typeSet[0]; } - if (typeSet.length === 2) { + if (typeSet.length === 2 && !(flags & IntersectionFlags.NoConstraintReduction)) { const typeVarIndex = typeSet[0].flags & TypeFlags.TypeVariable ? 0 : 1; const typeVariable = typeSet[typeVarIndex]; const primitiveType = typeSet[1 - typeVarIndex]; @@ -17556,7 +17557,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } } - const id = getTypeListId(typeSet) + getAliasId(aliasSymbol, aliasTypeArguments); + const id = getTypeListId(typeSet) + (flags & IntersectionFlags.NoConstraintReduction ? "*" : getAliasId(aliasSymbol, aliasTypeArguments)); let result = intersectionTypes.get(id); if (!result) { if (includes & TypeFlags.Union) { @@ -17564,16 +17565,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // When the intersection creates a reduced set (which might mean that *all* union types have // disappeared), we restart the operation to get a new set of combined flags. Once we have // reduced we'll never reduce again, so this occurs at most once. - result = getIntersectionType(typeSet, aliasSymbol, aliasTypeArguments); + result = getIntersectionType(typeSet, flags, aliasSymbol, aliasTypeArguments); } else if (every(typeSet, t => !!(t.flags & TypeFlags.Union && (t as UnionType).types[0].flags & TypeFlags.Undefined))) { const containedUndefinedType = some(typeSet, containsMissingType) ? missingType : undefinedType; removeFromEach(typeSet, TypeFlags.Undefined); - result = getUnionType([getIntersectionType(typeSet), containedUndefinedType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments); + result = getUnionType([getIntersectionType(typeSet, flags), containedUndefinedType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments); } else if (every(typeSet, t => !!(t.flags & TypeFlags.Union && ((t as UnionType).types[0].flags & TypeFlags.Null || (t as UnionType).types[1].flags & TypeFlags.Null)))) { removeFromEach(typeSet, TypeFlags.Null); - result = getUnionType([getIntersectionType(typeSet), nullType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments); + result = getUnionType([getIntersectionType(typeSet, flags), nullType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments); } else if (typeSet.length >= 4) { // When we have four or more constituents, some of which are unions, we employ a "divide and conquer" strategy @@ -17581,7 +17582,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // unions of intersections than the full cartesian product (due to some intersections becoming `never`), this can // dramatically reduce the overall work. const middle = Math.floor(typeSet.length / 2); - result = getIntersectionType([getIntersectionType(typeSet.slice(0, middle)), getIntersectionType(typeSet.slice(middle))], aliasSymbol, aliasTypeArguments); + result = getIntersectionType([getIntersectionType(typeSet.slice(0, middle), flags), getIntersectionType(typeSet.slice(middle), flags)], flags, aliasSymbol, aliasTypeArguments); } else { // We are attempting to construct a type of the form X & (A | B) & (C | D). Transform this into a type of @@ -17590,7 +17591,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!checkCrossProductUnion(typeSet)) { return errorType; } - const constituents = getCrossProductIntersections(typeSet); + const constituents = getCrossProductIntersections(typeSet, flags); // We attach a denormalized origin type when at least one constituent of the cross-product union is an // intersection (i.e. when the intersection didn't just reduce one or more unions to smaller unions) and // the denormalized origin has fewer constituents than the union itself. @@ -17620,7 +17621,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return true; } - function getCrossProductIntersections(types: readonly Type[]) { + function getCrossProductIntersections(types: readonly Type[], flags: IntersectionFlags) { const count = getCrossProductUnionSize(types); const intersections: Type[] = []; for (let i = 0; i < count; i++) { @@ -17634,7 +17635,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { n = Math.floor(n / length); } } - const t = getIntersectionType(constituents); + const t = getIntersectionType(constituents, flags); if (!(t.flags & TypeFlags.Never)) intersections.push(t); } return intersections; @@ -17661,7 +17662,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const emptyIndex = types.length === 2 ? types.indexOf(emptyTypeLiteralType) : -1; const t = emptyIndex >= 0 ? types[1 - emptyIndex] : unknownType; const noSupertypeReduction = !!(t.flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt) || t.flags & TypeFlags.TemplateLiteral && isPatternLiteralType(t)); - links.resolvedType = getIntersectionType(types, aliasSymbol, getTypeArgumentsForAliasSymbol(aliasSymbol), noSupertypeReduction); + links.resolvedType = getIntersectionType(types, noSupertypeReduction ? IntersectionFlags.NoSupertypeReduction : 0, aliasSymbol, getTypeArgumentsForAliasSymbol(aliasSymbol)); } return links.resolvedType; } @@ -18541,7 +18542,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return undefined; } return accessFlags & AccessFlags.Writing - ? getIntersectionType(propTypes, aliasSymbol, aliasTypeArguments) + ? getIntersectionType(propTypes, IntersectionFlags.None, aliasSymbol, aliasTypeArguments) : getUnionType(propTypes, UnionReduction.Literal, aliasSymbol, aliasTypeArguments); } return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, indexType, accessNode, accessFlags | AccessFlags.CacheSymbol | AccessFlags.ReportDeprecated); @@ -19892,7 +19893,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const newAliasSymbol = aliasSymbol || type.aliasSymbol; const newAliasTypeArguments = aliasSymbol ? aliasTypeArguments : instantiateTypes(type.aliasTypeArguments, mapper); return flags & TypeFlags.Intersection || origin && origin.flags & TypeFlags.Intersection ? - getIntersectionType(newTypes, newAliasSymbol, newAliasTypeArguments) : + getIntersectionType(newTypes, IntersectionFlags.None, newAliasSymbol, newAliasTypeArguments) : getUnionType(newTypes, UnionReduction.Literal, newAliasSymbol, newAliasTypeArguments); } if (flags & TypeFlags.Index) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f22978b464898..2725d2d086beb 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5377,6 +5377,13 @@ export const enum UnionReduction { Subtype, } +/** @internal */ +export const enum IntersectionFlags { + None = 0, + NoSupertypeReduction = 1 << 0, + NoConstraintReduction = 1 << 1, +} + // dprint-ignore /** @internal */ export const enum ContextFlags { From 505fe38cf899afb388e23a528acc03ad59e46f00 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 2 May 2024 08:56:59 -0700 Subject: [PATCH 2/4] Accept new baselines --- tests/baselines/reference/intersectionsOfLargeUnions.types | 1 - tests/baselines/reference/intersectionsOfLargeUnions2.types | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/baselines/reference/intersectionsOfLargeUnions.types b/tests/baselines/reference/intersectionsOfLargeUnions.types index 6feae7ef8cd07..7e38db8866105 100644 --- a/tests/baselines/reference/intersectionsOfLargeUnions.types +++ b/tests/baselines/reference/intersectionsOfLargeUnions.types @@ -1,7 +1,6 @@ //// [tests/cases/compiler/intersectionsOfLargeUnions.ts] //// === Performance Stats === -Strict subtype cache: 1,000 Assignability cache: 1,000 Type Count: 2,500 diff --git a/tests/baselines/reference/intersectionsOfLargeUnions2.types b/tests/baselines/reference/intersectionsOfLargeUnions2.types index d8c94c0aeedfd..606f2287595a3 100644 --- a/tests/baselines/reference/intersectionsOfLargeUnions2.types +++ b/tests/baselines/reference/intersectionsOfLargeUnions2.types @@ -1,7 +1,6 @@ //// [tests/cases/compiler/intersectionsOfLargeUnions2.ts] //// === Performance Stats === -Strict subtype cache: 1,000 Assignability cache: 1,000 Type Count: 2,500 From 16373dfdbf560d27e85899d30844656ecb10465c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 2 May 2024 09:01:55 -0700 Subject: [PATCH 3/4] Add tests --- .../intersectionConstraintReduction.symbols | 136 ++++++++++++++++++ .../intersectionConstraintReduction.types | 65 +++++++++ .../intersectionConstraintReduction.ts | 42 ++++++ 3 files changed, 243 insertions(+) create mode 100644 tests/baselines/reference/intersectionConstraintReduction.symbols create mode 100644 tests/baselines/reference/intersectionConstraintReduction.types create mode 100644 tests/cases/compiler/intersectionConstraintReduction.ts diff --git a/tests/baselines/reference/intersectionConstraintReduction.symbols b/tests/baselines/reference/intersectionConstraintReduction.symbols new file mode 100644 index 0000000000000..6b2c758461c9b --- /dev/null +++ b/tests/baselines/reference/intersectionConstraintReduction.symbols @@ -0,0 +1,136 @@ +//// [tests/cases/compiler/intersectionConstraintReduction.ts] //// + +=== intersectionConstraintReduction.ts === +type Test1 = +>Test1 : Symbol(Test1, Decl(intersectionConstraintReduction.ts, 0, 0)) +>K1 : Symbol(K1, Decl(intersectionConstraintReduction.ts, 0, 11)) +>K2 : Symbol(K2, Decl(intersectionConstraintReduction.ts, 0, 32)) + + MustBeKey & K1 & K2>; +>MustBeKey : Symbol(MustBeKey, Decl(intersectionConstraintReduction.ts, 4, 48)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>K1 : Symbol(K1, Decl(intersectionConstraintReduction.ts, 0, 11)) +>K1 : Symbol(K1, Decl(intersectionConstraintReduction.ts, 0, 11)) +>K2 : Symbol(K2, Decl(intersectionConstraintReduction.ts, 0, 32)) + +type Test2 = +>Test2 : Symbol(Test2, Decl(intersectionConstraintReduction.ts, 1, 48)) +>K1 : Symbol(K1, Decl(intersectionConstraintReduction.ts, 3, 11)) +>K2 : Symbol(K2, Decl(intersectionConstraintReduction.ts, 3, 32)) + + MustBeKey>; +>MustBeKey : Symbol(MustBeKey, Decl(intersectionConstraintReduction.ts, 4, 48)) +>K1 : Symbol(K1, Decl(intersectionConstraintReduction.ts, 3, 11)) +>K2 : Symbol(K2, Decl(intersectionConstraintReduction.ts, 3, 32)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>K1 : Symbol(K1, Decl(intersectionConstraintReduction.ts, 3, 11)) + +type MustBeKey = K; +>MustBeKey : Symbol(MustBeKey, Decl(intersectionConstraintReduction.ts, 4, 48)) +>K : Symbol(K, Decl(intersectionConstraintReduction.ts, 6, 15)) +>K : Symbol(K, Decl(intersectionConstraintReduction.ts, 6, 15)) + +// https://github.com/microsoft/TypeScript/issues/58370 + +type AnyKey = number | string | symbol; +>AnyKey : Symbol(AnyKey, Decl(intersectionConstraintReduction.ts, 6, 40)) + +type ReturnTypeKeyof = Obj extends object +>ReturnTypeKeyof : Symbol(ReturnTypeKeyof, Decl(intersectionConstraintReduction.ts, 10, 39)) +>Obj : Symbol(Obj, Decl(intersectionConstraintReduction.ts, 12, 21)) +>Obj : Symbol(Obj, Decl(intersectionConstraintReduction.ts, 12, 21)) + + ? [keyof Obj] extends [never] +>Obj : Symbol(Obj, Decl(intersectionConstraintReduction.ts, 12, 21)) + + ? never + : { [Key in keyof Obj as string]-?: () => Key }[string] +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 15, 13)) +>Obj : Symbol(Obj, Decl(intersectionConstraintReduction.ts, 12, 21)) +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 15, 13)) + + : never; + +type KeyIfSignatureOfObject< +>KeyIfSignatureOfObject : Symbol(KeyIfSignatureOfObject, Decl(intersectionConstraintReduction.ts, 16, 12)) + + Obj extends object, +>Obj : Symbol(Obj, Decl(intersectionConstraintReduction.ts, 18, 28)) + + Key extends AnyKey, +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 19, 23)) +>AnyKey : Symbol(AnyKey, Decl(intersectionConstraintReduction.ts, 6, 40)) + + ReturnTypeKeys = ReturnTypeKeyof, +>ReturnTypeKeys : Symbol(ReturnTypeKeys, Decl(intersectionConstraintReduction.ts, 20, 23)) +>ReturnTypeKeyof : Symbol(ReturnTypeKeyof, Decl(intersectionConstraintReduction.ts, 10, 39)) +>Obj : Symbol(Obj, Decl(intersectionConstraintReduction.ts, 18, 28)) + +> = ReturnTypeKeys extends () => Key ? ((() => Key) extends ReturnTypeKeys ? Key : never) : never; +>ReturnTypeKeys : Symbol(ReturnTypeKeys, Decl(intersectionConstraintReduction.ts, 20, 23)) +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 19, 23)) +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 19, 23)) +>ReturnTypeKeys : Symbol(ReturnTypeKeys, Decl(intersectionConstraintReduction.ts, 20, 23)) +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 19, 23)) + +export type Reduced1 = +>Reduced1 : Symbol(Reduced1, Decl(intersectionConstraintReduction.ts, 22, 98)) +>Obj : Symbol(Obj, Decl(intersectionConstraintReduction.ts, 24, 21)) +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 24, 40)) +>AnyKey : Symbol(AnyKey, Decl(intersectionConstraintReduction.ts, 6, 40)) +>Value : Symbol(Value, Decl(intersectionConstraintReduction.ts, 24, 60)) +>ObjKeys : Symbol(ObjKeys, Decl(intersectionConstraintReduction.ts, 24, 67)) +>Obj : Symbol(Obj, Decl(intersectionConstraintReduction.ts, 24, 21)) +>Obj : Symbol(Obj, Decl(intersectionConstraintReduction.ts, 24, 21)) + + Key extends KeyIfSignatureOfObject +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 24, 40)) +>KeyIfSignatureOfObject : Symbol(KeyIfSignatureOfObject, Decl(intersectionConstraintReduction.ts, 16, 12)) +>Obj : Symbol(Obj, Decl(intersectionConstraintReduction.ts, 24, 21)) +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 24, 40)) + + ? Key extends ObjKeys +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 24, 40)) +>ObjKeys : Symbol(ObjKeys, Decl(intersectionConstraintReduction.ts, 24, 67)) + + ? { [K in Key]: Value } +>K : Symbol(K, Decl(intersectionConstraintReduction.ts, 27, 17)) +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 24, 40)) +>Value : Symbol(Value, Decl(intersectionConstraintReduction.ts, 24, 60)) + + : never + : never; + +export type Reduced2 = +>Reduced2 : Symbol(Reduced2, Decl(intersectionConstraintReduction.ts, 29, 16)) +>Obj : Symbol(Obj, Decl(intersectionConstraintReduction.ts, 31, 21)) +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 31, 40)) +>AnyKey : Symbol(AnyKey, Decl(intersectionConstraintReduction.ts, 6, 40)) +>Value : Symbol(Value, Decl(intersectionConstraintReduction.ts, 31, 60)) +>ObjKeys : Symbol(ObjKeys, Decl(intersectionConstraintReduction.ts, 31, 67)) +>Obj : Symbol(Obj, Decl(intersectionConstraintReduction.ts, 31, 21)) +>Obj : Symbol(Obj, Decl(intersectionConstraintReduction.ts, 31, 21)) + + Key extends AnyKey +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 31, 40)) +>AnyKey : Symbol(AnyKey, Decl(intersectionConstraintReduction.ts, 6, 40)) + + ? Key extends KeyIfSignatureOfObject +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 31, 40)) +>KeyIfSignatureOfObject : Symbol(KeyIfSignatureOfObject, Decl(intersectionConstraintReduction.ts, 16, 12)) +>Obj : Symbol(Obj, Decl(intersectionConstraintReduction.ts, 31, 21)) +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 31, 40)) + + ? Key extends ObjKeys +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 31, 40)) +>ObjKeys : Symbol(ObjKeys, Decl(intersectionConstraintReduction.ts, 31, 67)) + + ? { [K in Key]: Value } +>K : Symbol(K, Decl(intersectionConstraintReduction.ts, 35, 20)) +>Key : Symbol(Key, Decl(intersectionConstraintReduction.ts, 31, 40)) +>Value : Symbol(Value, Decl(intersectionConstraintReduction.ts, 31, 60)) + + : never + : never + : never; + diff --git a/tests/baselines/reference/intersectionConstraintReduction.types b/tests/baselines/reference/intersectionConstraintReduction.types new file mode 100644 index 0000000000000..b2b62bf6632ef --- /dev/null +++ b/tests/baselines/reference/intersectionConstraintReduction.types @@ -0,0 +1,65 @@ +//// [tests/cases/compiler/intersectionConstraintReduction.ts] //// + +=== intersectionConstraintReduction.ts === +type Test1 = +>Test1 : Extract & K1 & K2 +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + MustBeKey & K1 & K2>; + +type Test2 = +>Test2 : K1 & K2 & Extract +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + MustBeKey>; + +type MustBeKey = K; +>MustBeKey : K +> : ^ + +// https://github.com/microsoft/TypeScript/issues/58370 + +type AnyKey = number | string | symbol; +>AnyKey : AnyKey +> : ^^^^^^ + +type ReturnTypeKeyof = Obj extends object +>ReturnTypeKeyof : ReturnTypeKeyof +> : ^^^^^^^^^^^^^^^^^^^^ + + ? [keyof Obj] extends [never] + ? never + : { [Key in keyof Obj as string]-?: () => Key }[string] + : never; + +type KeyIfSignatureOfObject< +>KeyIfSignatureOfObject : KeyIfSignatureOfObject +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Obj extends object, + Key extends AnyKey, + ReturnTypeKeys = ReturnTypeKeyof, +> = ReturnTypeKeys extends () => Key ? ((() => Key) extends ReturnTypeKeys ? Key : never) : never; + +export type Reduced1 = +>Reduced1 : Reduced1 +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Key extends KeyIfSignatureOfObject + ? Key extends ObjKeys + ? { [K in Key]: Value } + : never + : never; + +export type Reduced2 = +>Reduced2 : Reduced2 +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Key extends AnyKey + ? Key extends KeyIfSignatureOfObject + ? Key extends ObjKeys + ? { [K in Key]: Value } + : never + : never + : never; + diff --git a/tests/cases/compiler/intersectionConstraintReduction.ts b/tests/cases/compiler/intersectionConstraintReduction.ts new file mode 100644 index 0000000000000..9c4a6f935d89c --- /dev/null +++ b/tests/cases/compiler/intersectionConstraintReduction.ts @@ -0,0 +1,42 @@ +// @strict: true +// @noEmit: true + +type Test1 = + MustBeKey & K1 & K2>; + +type Test2 = + MustBeKey>; + +type MustBeKey = K; + +// https://github.com/microsoft/TypeScript/issues/58370 + +type AnyKey = number | string | symbol; + +type ReturnTypeKeyof = Obj extends object + ? [keyof Obj] extends [never] + ? never + : { [Key in keyof Obj as string]-?: () => Key }[string] + : never; + +type KeyIfSignatureOfObject< + Obj extends object, + Key extends AnyKey, + ReturnTypeKeys = ReturnTypeKeyof, +> = ReturnTypeKeys extends () => Key ? ((() => Key) extends ReturnTypeKeys ? Key : never) : never; + +export type Reduced1 = + Key extends KeyIfSignatureOfObject + ? Key extends ObjKeys + ? { [K in Key]: Value } + : never + : never; + +export type Reduced2 = + Key extends AnyKey + ? Key extends KeyIfSignatureOfObject + ? Key extends ObjKeys + ? { [K in Key]: Value } + : never + : never + : never; From cc202c10536de62f917b78e8d8a3ba97d9467f2d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 2 May 2024 09:02:29 -0700 Subject: [PATCH 4/4] Formatting --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8a79207820984..4839d97d7ab08 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -446,6 +446,7 @@ import { InterfaceType, InterfaceTypeWithDeclaredMembers, InternalSymbolName, + IntersectionFlags, IntersectionType, IntersectionTypeNode, intrinsicTagNameToString, @@ -1104,7 +1105,6 @@ import { WideningContext, WithStatement, YieldExpression, - IntersectionFlags, } from "./_namespaces/ts"; import * as moduleSpecifiers from "./_namespaces/ts.moduleSpecifiers"; import * as performance from "./_namespaces/ts.performance";