From 3bc98c2765066b25bf8dac6b187fa095a92cb6d4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Feb 2026 00:09:03 +0000
Subject: [PATCH 1/2] Initial plan
From 1941292b7733f008b9bfa83315da5f200f48a8a4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Feb 2026 00:44:00 +0000
Subject: [PATCH 2/2] Port TypeScript PR #60528: Fix crash related to index
type deferral on generic mapped types with name types
Changes:
- Simplify shouldDeferIndexType to check for name type existence instead of distributive name type
- Remove hasDistributiveNameType function (no longer needed)
- Add TypeFlagsIndex to TypeFlagsSimplifiable
- Handle generic mapped types with name types in computeBaseConstraint
- Route TypeFlagsIndex through getResolvedBaseConstraint in getBaseConstraintOfType
- Unskip mappedTypeAsClauseRecursiveNoCrash1.ts test
- Accept baseline changes
Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
---
internal/checker/checker.go | 50 ++------
internal/checker/types.go | 2 +-
internal/testrunner/compiler_runner.go | 3 -
.../keyRemappingKeyofResult2.errors.txt | 51 --------
.../keyRemappingKeyofResult2.errors.txt.diff | 55 --------
...appedTypeAsClauseRecursiveNoCrash1.symbols | 121 ++++++++++++++++++
.../mappedTypeAsClauseRecursiveNoCrash1.types | 73 +++++++++++
.../mappedTypeConstraints2.errors.txt | 13 +-
.../mappedTypeConstraints2.errors.txt.diff | 30 +----
.../conformance/mappedTypeConstraints2.types | 10 +-
.../mappedTypeConstraints2.types.diff | 31 +----
11 files changed, 217 insertions(+), 222 deletions(-)
delete mode 100644 testdata/baselines/reference/submodule/compiler/keyRemappingKeyofResult2.errors.txt
delete mode 100644 testdata/baselines/reference/submodule/compiler/keyRemappingKeyofResult2.errors.txt.diff
create mode 100644 testdata/baselines/reference/submodule/conformance/mappedTypeAsClauseRecursiveNoCrash1.symbols
create mode 100644 testdata/baselines/reference/submodule/conformance/mappedTypeAsClauseRecursiveNoCrash1.types
diff --git a/internal/checker/checker.go b/internal/checker/checker.go
index 9dac7a0507b..87110af30a3 100644
--- a/internal/checker/checker.go
+++ b/internal/checker/checker.go
@@ -26200,46 +26200,11 @@ func (c *Checker) getSubstitutionIntersection(t *Type) *Type {
func (c *Checker) shouldDeferIndexType(t *Type, indexFlags IndexFlags) bool {
return t.flags&TypeFlagsInstantiableNonPrimitive != 0 ||
c.isGenericTupleType(t) ||
- c.isGenericMappedType(t) && (!c.hasDistributiveNameType(t) || c.getMappedTypeNameTypeKind(t) == MappedTypeNameTypeKindRemapping) ||
+ c.isGenericMappedType(t) && c.getNameTypeFromMappedType(t) != nil ||
t.flags&TypeFlagsUnion != 0 && indexFlags&IndexFlagsNoReducibleCheck == 0 && c.isGenericReducibleType(t) ||
t.flags&TypeFlagsIntersection != 0 && c.maybeTypeOfKind(t, TypeFlagsInstantiable) && core.Some(t.Types(), c.IsEmptyAnonymousObjectType)
}
-// Ordinarily we reduce a keyof M, where M is a mapped type { [P in K as N
]: X }, to simply N. This however presumes
-// that N distributes over union types, i.e. that N is equivalent to N | N | N. Specifically, we only
-// want to perform the reduction when the name type of a mapped type is distributive with respect to the type variable
-// introduced by the 'in' clause of the mapped type. Note that non-generic types are considered to be distributive because
-// they're the same type regardless of what's being distributed over.
-func (c *Checker) hasDistributiveNameType(mappedType *Type) bool {
- typeVariable := c.getTypeParameterFromMappedType(mappedType)
- var isDistributive func(*Type) bool
- isDistributive = func(t *Type) bool {
- switch {
- case t.flags&(TypeFlagsAnyOrUnknown|TypeFlagsPrimitive|TypeFlagsNever|TypeFlagsTypeParameter|TypeFlagsObject|TypeFlagsNonPrimitive) != 0:
- return true
- case t.flags&TypeFlagsConditional != 0:
- return t.AsConditionalType().root.isDistributive && t.AsConditionalType().checkType == typeVariable
- case t.flags&TypeFlagsUnionOrIntersection != 0:
- return core.Every(t.Types(), isDistributive)
- case t.flags&TypeFlagsTemplateLiteral != 0:
- return core.Every(t.AsTemplateLiteralType().types, isDistributive)
- case t.flags&TypeFlagsIndexedAccess != 0:
- return isDistributive(t.AsIndexedAccessType().objectType) && isDistributive(t.AsIndexedAccessType().indexType)
- case t.flags&TypeFlagsSubstitution != 0:
- return isDistributive(t.AsSubstitutionType().baseType) && isDistributive(t.AsSubstitutionType().constraint)
- case t.flags&TypeFlagsStringMapping != 0:
- return isDistributive(t.Target())
- default:
- return false
- }
- }
- nameType := c.getNameTypeFromMappedType(mappedType)
- if nameType == nil {
- nameType = typeVariable
- }
- return isDistributive(nameType)
-}
-
func (c *Checker) getMappedTypeNameTypeKind(t *Type) MappedTypeNameTypeKind {
nameType := c.getNameTypeFromMappedType(t)
if nameType == nil {
@@ -26292,7 +26257,7 @@ func (c *Checker) getIndexTypeForMappedType(t *Type, indexFlags IndexFlags) *Typ
// a circular definition. For this reason, we only eagerly manifest the keys if the constraint is non-generic.
if c.isGenericIndexType(constraintType) {
if c.isMappedTypeWithKeyofConstraintDeclaration(t) {
- // We have a generic index and a homomorphic mapping (but a distributive key remapping) - we need to defer
+ // We have a generic index and a homomorphic mapping and a key remapping - we need to defer
// the whole `keyof whatever` for later since it's not safe to resolve the shape of modifier type.
return c.getIndexTypeForGenericType(t, indexFlags)
}
@@ -26829,16 +26794,13 @@ func (c *Checker) getBaseConstraintOrType(t *Type) *Type {
}
func (c *Checker) getBaseConstraintOfType(t *Type) *Type {
- if t.flags&(TypeFlagsInstantiableNonPrimitive|TypeFlagsUnionOrIntersection|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 || c.isGenericTupleType(t) {
+ if t.flags&(TypeFlagsInstantiableNonPrimitive|TypeFlagsUnionOrIntersection|TypeFlagsTemplateLiteral|TypeFlagsStringMapping|TypeFlagsIndex) != 0 || c.isGenericTupleType(t) {
constraint := c.getResolvedBaseConstraint(t, nil)
if constraint != c.noConstraintType && constraint != c.circularConstraintType {
return constraint
}
return nil
}
- if t.flags&TypeFlagsIndex != 0 {
- return c.stringNumberSymbolType
- }
return nil
}
@@ -26919,6 +26881,12 @@ func (c *Checker) computeBaseConstraint(t *Type, stack []RecursionId) *Type {
}
return nil
case t.flags&TypeFlagsIndex != 0:
+ if c.isGenericMappedType(t.AsIndexType().target) {
+ mappedType := t.AsIndexType().target
+ if c.getNameTypeFromMappedType(mappedType) != nil && !c.isMappedTypeWithKeyofConstraintDeclaration(mappedType) {
+ return c.getNextBaseConstraint(c.getIndexTypeForMappedType(mappedType, IndexFlagsNone), stack)
+ }
+ }
return c.stringNumberSymbolType
case t.flags&TypeFlagsTemplateLiteral != 0:
types := t.Types()
diff --git a/internal/checker/types.go b/internal/checker/types.go
index 3e2aafbd4c3..8900314987d 100644
--- a/internal/checker/types.go
+++ b/internal/checker/types.go
@@ -450,7 +450,7 @@ const (
TypeFlagsInstantiable = TypeFlagsInstantiableNonPrimitive | TypeFlagsInstantiablePrimitive
TypeFlagsStructuredOrInstantiable = TypeFlagsStructuredType | TypeFlagsInstantiable
TypeFlagsObjectFlagsType = TypeFlagsAny | TypeFlagsNullable | TypeFlagsNever | TypeFlagsObject | TypeFlagsUnion | TypeFlagsIntersection
- TypeFlagsSimplifiable = TypeFlagsIndexedAccess | TypeFlagsConditional
+ TypeFlagsSimplifiable = TypeFlagsIndexedAccess | TypeFlagsConditional | TypeFlagsIndex
TypeFlagsSingleton = TypeFlagsAny | TypeFlagsUnknown | TypeFlagsString | TypeFlagsNumber | TypeFlagsBoolean | TypeFlagsBigInt | TypeFlagsESSymbol | TypeFlagsVoid | TypeFlagsUndefined | TypeFlagsNull | TypeFlagsNever | TypeFlagsNonPrimitive
// 'TypeFlagsNarrowable' types are types where narrowing actually narrows.
// This *should* be every type other than null, undefined, void, and never
diff --git a/internal/testrunner/compiler_runner.go b/internal/testrunner/compiler_runner.go
index 4c6b1ad752a..fc4a62a19a5 100644
--- a/internal/testrunner/compiler_runner.go
+++ b/internal/testrunner/compiler_runner.go
@@ -84,9 +84,6 @@ func (r *CompilerBaselineRunner) EnumerateTestFiles() []string {
}
var skippedTests = []string{
- // Broken until further porting work is done
- "mappedTypeAsClauseRecursiveNoCrash1.ts",
-
// Flaky
"for-of29.ts",
diff --git a/testdata/baselines/reference/submodule/compiler/keyRemappingKeyofResult2.errors.txt b/testdata/baselines/reference/submodule/compiler/keyRemappingKeyofResult2.errors.txt
deleted file mode 100644
index 6ee25e23d47..00000000000
--- a/testdata/baselines/reference/submodule/compiler/keyRemappingKeyofResult2.errors.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-keyRemappingKeyofResult2.ts(23,7): error TS2344: Type 'Values<{ [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }>' does not satisfy the constraint 'ProvidedActor'.
- Types of property 'src' are incompatible.
- Type 'keyof { [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }' is not assignable to type 'string'.
- Type 'string | number | symbol' is not assignable to type 'string'.
- Type 'number' is not assignable to type 'string'.
-
-
-==== keyRemappingKeyofResult2.ts (1 errors) ====
- // https://github.com/microsoft/TypeScript/issues/56239
-
- type Values = T[keyof T];
-
- type ProvidedActor = {
- src: string;
- logic: unknown;
- };
-
- interface StateMachineConfig {
- invoke: {
- src: TActors["src"];
- };
- }
-
- declare function setup>(_: {
- actors: {
- [K in keyof TActors]: TActors[K];
- };
- }): {
- createMachine: (
- config: StateMachineConfig<
- Values<{
- ~~~~~~~~
- [K in keyof TActors as K & string]: {
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- src: K;
- ~~~~~~~~~~~~~~~~~
- logic: TActors[K];
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- };
- ~~~~~~~~~~
- }>
- ~~~~~~~~
-!!! error TS2344: Type 'Values<{ [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }>' does not satisfy the constraint 'ProvidedActor'.
-!!! error TS2344: Types of property 'src' are incompatible.
-!!! error TS2344: Type 'keyof { [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }' is not assignable to type 'string'.
-!!! error TS2344: Type 'string | number | symbol' is not assignable to type 'string'.
-!!! error TS2344: Type 'number' is not assignable to type 'string'.
- >,
- ) => void;
- };
-
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/compiler/keyRemappingKeyofResult2.errors.txt.diff b/testdata/baselines/reference/submodule/compiler/keyRemappingKeyofResult2.errors.txt.diff
deleted file mode 100644
index 44d7535e0fa..00000000000
--- a/testdata/baselines/reference/submodule/compiler/keyRemappingKeyofResult2.errors.txt.diff
+++ /dev/null
@@ -1,55 +0,0 @@
---- old.keyRemappingKeyofResult2.errors.txt
-+++ new.keyRemappingKeyofResult2.errors.txt
-@@= skipped -0, +0 lines =@@
--
-+keyRemappingKeyofResult2.ts(23,7): error TS2344: Type 'Values<{ [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }>' does not satisfy the constraint 'ProvidedActor'.
-+ Types of property 'src' are incompatible.
-+ Type 'keyof { [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }' is not assignable to type 'string'.
-+ Type 'string | number | symbol' is not assignable to type 'string'.
-+ Type 'number' is not assignable to type 'string'.
-+
-+
-+==== keyRemappingKeyofResult2.ts (1 errors) ====
-+ // https://github.com/microsoft/TypeScript/issues/56239
-+
-+ type Values = T[keyof T];
-+
-+ type ProvidedActor = {
-+ src: string;
-+ logic: unknown;
-+ };
-+
-+ interface StateMachineConfig {
-+ invoke: {
-+ src: TActors["src"];
-+ };
-+ }
-+
-+ declare function setup>(_: {
-+ actors: {
-+ [K in keyof TActors]: TActors[K];
-+ };
-+ }): {
-+ createMachine: (
-+ config: StateMachineConfig<
-+ Values<{
-+ ~~~~~~~~
-+ [K in keyof TActors as K & string]: {
-+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-+ src: K;
-+ ~~~~~~~~~~~~~~~~~
-+ logic: TActors[K];
-+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-+ };
-+ ~~~~~~~~~~
-+ }>
-+ ~~~~~~~~
-+!!! error TS2344: Type 'Values<{ [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }>' does not satisfy the constraint 'ProvidedActor'.
-+!!! error TS2344: Types of property 'src' are incompatible.
-+!!! error TS2344: Type 'keyof { [K in keyof TActors as K & string]: { src: K; logic: TActors[K]; }; }' is not assignable to type 'string'.
-+!!! error TS2344: Type 'string | number | symbol' is not assignable to type 'string'.
-+!!! error TS2344: Type 'number' is not assignable to type 'string'.
-+ >,
-+ ) => void;
-+ };
-+
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/conformance/mappedTypeAsClauseRecursiveNoCrash1.symbols b/testdata/baselines/reference/submodule/conformance/mappedTypeAsClauseRecursiveNoCrash1.symbols
new file mode 100644
index 00000000000..2aa6ec2b1a5
--- /dev/null
+++ b/testdata/baselines/reference/submodule/conformance/mappedTypeAsClauseRecursiveNoCrash1.symbols
@@ -0,0 +1,121 @@
+//// [tests/cases/conformance/types/mapped/mappedTypeAsClauseRecursiveNoCrash1.ts] ////
+
+=== mappedTypeAsClauseRecursiveNoCrash1.ts ===
+// https://github.com/microsoft/TypeScript/issues/60476
+
+export type FlattenType = {
+>FlattenType : Symbol(FlattenType, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 0, 0))
+>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 2, 24))
+>Target : Symbol(Target, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 2, 46))
+
+ [Key in keyof Source as Key extends string
+>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 3, 3))
+>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 2, 24))
+>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 3, 3))
+
+ ? Source[Key] extends object
+>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 2, 24))
+>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 3, 3))
+
+ ? `${Key}.${keyof FlattenType & string}`
+>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 3, 3))
+>FlattenType : Symbol(FlattenType, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 0, 0))
+>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 2, 24))
+>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 3, 3))
+>Target : Symbol(Target, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 2, 46))
+
+ : Key
+>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 3, 3))
+
+ : never]-?: Target;
+>Target : Symbol(Target, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 2, 46))
+
+};
+
+type FieldSelect = {
+>FieldSelect : Symbol(FieldSelect, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 8, 2))
+
+ table: string;
+>table : Symbol(table, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 10, 20))
+
+ field: string;
+>field : Symbol(field, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 11, 16))
+
+};
+
+type Address = {
+>Address : Symbol(Address, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 13, 2))
+
+ postCode: string;
+>postCode : Symbol(postCode, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 15, 16))
+
+ description: string;
+>description : Symbol(description, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 16, 19))
+
+ address: string;
+>address : Symbol(address, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 17, 22))
+
+};
+
+type User = {
+>User : Symbol(User, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 19, 2))
+
+ id: number;
+>id : Symbol(id, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 21, 13))
+
+ name: string;
+>name : Symbol(name, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 22, 13))
+
+ address: Address;
+>address : Symbol(address, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 23, 15))
+>Address : Symbol(Address, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 13, 2))
+
+};
+
+type FlattenedUser = FlattenType;
+>FlattenedUser : Symbol(FlattenedUser, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 25, 2))
+>FlattenType : Symbol(FlattenType, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 0, 0))
+>User : Symbol(User, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 19, 2))
+>FieldSelect : Symbol(FieldSelect, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 8, 2))
+
+type FlattenedUserKeys = keyof FlattenType;
+>FlattenedUserKeys : Symbol(FlattenedUserKeys, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 27, 52))
+>FlattenType : Symbol(FlattenType, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 0, 0))
+>User : Symbol(User, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 19, 2))
+>FieldSelect : Symbol(FieldSelect, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 8, 2))
+
+export type FlattenTypeKeys = keyof {
+>FlattenTypeKeys : Symbol(FlattenTypeKeys, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 28, 62))
+>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 30, 28))
+>Target : Symbol(Target, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 30, 50))
+
+ [Key in keyof Source as Key extends string
+>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 31, 3))
+>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 30, 28))
+>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 31, 3))
+
+ ? Source[Key] extends object
+>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 30, 28))
+>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 31, 3))
+
+ ? `${Key}.${keyof FlattenType & string}`
+>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 31, 3))
+>FlattenType : Symbol(FlattenType, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 0, 0))
+>Source : Symbol(Source, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 30, 28))
+>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 31, 3))
+>Target : Symbol(Target, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 30, 50))
+
+ : Key
+>Key : Symbol(Key, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 31, 3))
+
+ : never]-?: Target;
+>Target : Symbol(Target, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 30, 50))
+
+};
+
+type FlattenedUserKeys2 = FlattenTypeKeys;
+>FlattenedUserKeys2 : Symbol(FlattenedUserKeys2, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 36, 2))
+>FlattenTypeKeys : Symbol(FlattenTypeKeys, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 28, 62))
+>User : Symbol(User, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 19, 2))
+>FieldSelect : Symbol(FieldSelect, Decl(mappedTypeAsClauseRecursiveNoCrash1.ts, 8, 2))
+
diff --git a/testdata/baselines/reference/submodule/conformance/mappedTypeAsClauseRecursiveNoCrash1.types b/testdata/baselines/reference/submodule/conformance/mappedTypeAsClauseRecursiveNoCrash1.types
new file mode 100644
index 00000000000..cfd1546d3d3
--- /dev/null
+++ b/testdata/baselines/reference/submodule/conformance/mappedTypeAsClauseRecursiveNoCrash1.types
@@ -0,0 +1,73 @@
+//// [tests/cases/conformance/types/mapped/mappedTypeAsClauseRecursiveNoCrash1.ts] ////
+
+=== mappedTypeAsClauseRecursiveNoCrash1.ts ===
+// https://github.com/microsoft/TypeScript/issues/60476
+
+export type FlattenType = {
+>FlattenType : FlattenType
+
+ [Key in keyof Source as Key extends string
+ ? Source[Key] extends object
+ ? `${Key}.${keyof FlattenType & string}`
+ : Key
+ : never]-?: Target;
+};
+
+type FieldSelect = {
+>FieldSelect : FieldSelect
+
+ table: string;
+>table : string
+
+ field: string;
+>field : string
+
+};
+
+type Address = {
+>Address : Address
+
+ postCode: string;
+>postCode : string
+
+ description: string;
+>description : string
+
+ address: string;
+>address : string
+
+};
+
+type User = {
+>User : User
+
+ id: number;
+>id : number
+
+ name: string;
+>name : string
+
+ address: Address;
+>address : Address
+
+};
+
+type FlattenedUser = FlattenType;
+>FlattenedUser : FlattenType
+
+type FlattenedUserKeys = keyof FlattenType;
+>FlattenedUserKeys : "address.address" | "address.description" | "address.postCode" | "id" | "name"
+
+export type FlattenTypeKeys = keyof {
+>FlattenTypeKeys : keyof { [Key in keyof Source as Key extends string ? Source[Key] extends object ? `${Key}.${keyof FlattenType & string}` : Key : never]-?: Target; }
+
+ [Key in keyof Source as Key extends string
+ ? Source[Key] extends object
+ ? `${Key}.${keyof FlattenType & string}`
+ : Key
+ : never]-?: Target;
+};
+
+type FlattenedUserKeys2 = FlattenTypeKeys;
+>FlattenedUserKeys2 : "address.address" | "address.description" | "address.postCode" | "id" | "name"
+
diff --git a/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.errors.txt b/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.errors.txt
index bfc0fcbb830..bc2f5287b0a 100644
--- a/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.errors.txt
+++ b/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.errors.txt
@@ -4,17 +4,14 @@ mappedTypeConstraints2.ts(16,11): error TS2322: Type 'Mapped3[Uppercase]'
Type 'Mapped3[Uppercase]' is not assignable to type '{ a: K; }'.
Type 'Mapped3[string]' is not assignable to type '{ a: K; }'.
mappedTypeConstraints2.ts(42,7): error TS2322: Type 'Mapped6[keyof Mapped6]' is not assignable to type '`_${string}`'.
- Type 'Mapped6[string] | Mapped6[number] | Mapped6[symbol]' is not assignable to type '`_${string}`'.
- Type 'Mapped6[string]' is not assignable to type '`_${string}`'.
-mappedTypeConstraints2.ts(50,7): error TS2322: Type 'string | number | symbol' is not assignable to type '`_${string}`'.
- Type 'string' is not assignable to type '`_${string}`'.
+ Type 'Mapped6[`_${string}`]' is not assignable to type '`_${string}`'.
mappedTypeConstraints2.ts(59,57): error TS2322: Type 'Foo[`get${T}`]' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'Foo[`get${T}`]'.
mappedTypeConstraints2.ts(90,9): error TS2322: Type 'ObjectWithUnderscoredKeys[`_${K}`]' is not assignable to type 'true'.
Type 'ObjectWithUnderscoredKeys[`_${string}`]' is not assignable to type 'true'.
-==== mappedTypeConstraints2.ts (6 errors) ====
+==== mappedTypeConstraints2.ts (5 errors) ====
type Mapped1 = { [P in K]: { a: P } };
function f1(obj: Mapped1, key: K) {
@@ -66,8 +63,7 @@ mappedTypeConstraints2.ts(90,9): error TS2322: Type 'ObjectWithUnderscoredKeys[keyof Mapped6]' is not assignable to type '`_${string}`'.
-!!! error TS2322: Type 'Mapped6[string] | Mapped6[number] | Mapped6[symbol]' is not assignable to type '`_${string}`'.
-!!! error TS2322: Type 'Mapped6[string]' is not assignable to type '`_${string}`'.
+!!! error TS2322: Type 'Mapped6[`_${string}`]' is not assignable to type '`_${string}`'.
}
type Mapped7 = {
@@ -76,9 +72,6 @@ mappedTypeConstraints2.ts(90,9): error TS2322: Type 'ObjectWithUnderscoredKeys(obj: Mapped7, key: keyof Mapped7) {
let s: `_${string}` = obj[key];
- ~
-!!! error TS2322: Type 'string | number | symbol' is not assignable to type '`_${string}`'.
-!!! error TS2322: Type 'string' is not assignable to type '`_${string}`'.
}
// Repro from #47794
diff --git a/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.errors.txt.diff b/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.errors.txt.diff
index 08ef2058b9f..6706bb75872 100644
--- a/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.errors.txt.diff
+++ b/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.errors.txt.diff
@@ -6,39 +6,17 @@
mappedTypeConstraints2.ts(42,7): error TS2322: Type 'Mapped6[keyof Mapped6]' is not assignable to type '`_${string}`'.
- Type 'Mapped6[`_${K}`]' is not assignable to type '`_${string}`'.
- Type 'Mapped6[`_${string}`]' is not assignable to type '`_${string}`'.
-+ Type 'Mapped6[string] | Mapped6[number] | Mapped6[symbol]' is not assignable to type '`_${string}`'.
-+ Type 'Mapped6[string]' is not assignable to type '`_${string}`'.
-+mappedTypeConstraints2.ts(50,7): error TS2322: Type 'string | number | symbol' is not assignable to type '`_${string}`'.
-+ Type 'string' is not assignable to type '`_${string}`'.
++ Type 'Mapped6[`_${string}`]' is not assignable to type '`_${string}`'.
mappedTypeConstraints2.ts(59,57): error TS2322: Type 'Foo[`get${T}`]' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'Foo[`get${T}`]'.
mappedTypeConstraints2.ts(90,9): error TS2322: Type 'ObjectWithUnderscoredKeys[`_${K}`]' is not assignable to type 'true'.
- Type 'ObjectWithUnderscoredKeys[`_${string}`]' is not assignable to type 'true'.
-
-
--==== mappedTypeConstraints2.ts (5 errors) ====
-+==== mappedTypeConstraints2.ts (6 errors) ====
- type Mapped1 = { [P in K]: { a: P } };
-
- function f1(obj: Mapped1, key: K) {
-@@= skipped -60, +62 lines =@@
+@@= skipped -60, +59 lines =@@
let s: `_${string}` = obj[key]; // Error
~
!!! error TS2322: Type 'Mapped6[keyof Mapped6]' is not assignable to type '`_${string}`'.
-!!! error TS2322: Type 'Mapped6[`_${K}`]' is not assignable to type '`_${string}`'.
-!!! error TS2322: Type 'Mapped6[`_${string}`]' is not assignable to type '`_${string}`'.
-+!!! error TS2322: Type 'Mapped6[string] | Mapped6[number] | Mapped6[symbol]' is not assignable to type '`_${string}`'.
-+!!! error TS2322: Type 'Mapped6[string]' is not assignable to type '`_${string}`'.
- }
-
- type Mapped7 = {
-@@= skipped -10, +10 lines =@@
-
- function f7(obj: Mapped7, key: keyof Mapped7) {
- let s: `_${string}` = obj[key];
-+ ~
-+!!! error TS2322: Type 'string | number | symbol' is not assignable to type '`_${string}`'.
-+!!! error TS2322: Type 'string' is not assignable to type '`_${string}`'.
++!!! error TS2322: Type 'Mapped6[`_${string}`]' is not assignable to type '`_${string}`'.
}
- // Repro from #47794
\ No newline at end of file
+ type Mapped7 = {
\ No newline at end of file
diff --git a/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.types b/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.types
index 23e57759a7c..98ba265c1a1 100644
--- a/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.types
+++ b/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.types
@@ -77,15 +77,15 @@ type Mapped5 = {
};
function f5(obj: Mapped5, key: keyof Mapped5) {
->f5 : (obj: Mapped5, key: K extends `_${string}` ? K : never) => void
+>f5 : (obj: Mapped5, key: keyof Mapped5) => void
>obj : Mapped5
->key : K extends `_${string}` ? K : never
+>key : keyof Mapped5
let s: `_${string}` = obj[key];
>s : `_${string}`
->obj[key] : Mapped5[K extends `_${string}` ? K : never]
+>obj[key] : Mapped5[keyof Mapped5]
>obj : Mapped5
->key : K extends `_${string}` ? K : never
+>key : keyof Mapped5
}
// repro from #53066#issuecomment-1913384757
@@ -121,7 +121,7 @@ function f7(obj: Mapped7, key: keyof Mapped7) {
let s: `_${string}` = obj[key];
>s : `_${string}`
->obj[key] : string | number | symbol
+>obj[key] : Mapped7[keyof Mapped7]
>obj : Mapped7
>key : keyof Mapped7
}
diff --git a/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.types.diff b/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.types.diff
index c3d65cee6f9..79013af57bf 100644
--- a/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.types.diff
+++ b/testdata/baselines/reference/submodule/conformance/mappedTypeConstraints2.types.diff
@@ -9,36 +9,7 @@
>obj : Mapped4
>key : K
-@@= skipped -18, +18 lines =@@
- };
-
- function f5(obj: Mapped5, key: keyof Mapped5) {
-->f5 : (obj: Mapped5, key: keyof Mapped5) => void
-+>f5 : (obj: Mapped5, key: K extends `_${string}` ? K : never) => void
- >obj : Mapped5
-->key : keyof Mapped5
-+>key : K extends `_${string}` ? K : never
-
- let s: `_${string}` = obj[key];
- >s : `_${string}`
-->obj[key] : Mapped5[keyof Mapped5]
-+>obj[key] : Mapped5[K extends `_${string}` ? K : never]
- >obj : Mapped5
-->key : keyof Mapped5
-+>key : K extends `_${string}` ? K : never
- }
-
- // repro from #53066#issuecomment-1913384757
-@@= skipped -44, +44 lines =@@
-
- let s: `_${string}` = obj[key];
- >s : `_${string}`
-->obj[key] : Mapped7[keyof Mapped7]
-+>obj[key] : string | number | symbol
- >obj : Mapped7
- >key : keyof Mapped7
- }
-@@= skipped -48, +48 lines =@@
+@@= skipped -110, +110 lines =@@
>key : string
>val : any
>Object.entries(obj) : [string, any][]