diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 3d044fa5bf0f4..eeb112f2f225e 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -14749,6 +14749,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
undefined;
}
if (t.flags & TypeFlags.Index) {
+ if (isGenericMappedType((t as IndexType).type)) {
+ const mappedType = (t as IndexType).type as MappedType;
+ if (getNameTypeFromMappedType(mappedType) && !isMappedTypeWithKeyofConstraintDeclaration(mappedType)) {
+ return getBaseConstraint(getIndexTypeForMappedType(mappedType, IndexFlags.None));
+ }
+ }
return stringNumberSymbolType;
}
if (t.flags & TypeFlags.TemplateLiteral) {
@@ -18250,7 +18256,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// a circular definition. For this reason, we only eagerly manifest the keys if the constraint is non-generic.
if (isGenericIndexType(constraintType)) {
if (isMappedTypeWithKeyofConstraintDeclaration(type)) {
- // 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 getIndexTypeForGenericType(type, indexFlags);
}
@@ -18280,25 +18286,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
- // 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.
- function hasDistributiveNameType(mappedType: MappedType) {
- const typeVariable = getTypeParameterFromMappedType(mappedType);
- return isDistributive(getNameTypeFromMappedType(mappedType) || typeVariable);
- function isDistributive(type: Type): boolean {
- return type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.Primitive | TypeFlags.Never | TypeFlags.TypeParameter | TypeFlags.Object | TypeFlags.NonPrimitive) ? true :
- type.flags & TypeFlags.Conditional ? (type as ConditionalType).root.isDistributive && (type as ConditionalType).checkType === typeVariable :
- type.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral) ? every((type as UnionOrIntersectionType | TemplateLiteralType).types, isDistributive) :
- type.flags & TypeFlags.IndexedAccess ? isDistributive((type as IndexedAccessType).objectType) && isDistributive((type as IndexedAccessType).indexType) :
- type.flags & TypeFlags.Substitution ? isDistributive((type as SubstitutionType).baseType) && isDistributive((type as SubstitutionType).constraint) :
- type.flags & TypeFlags.StringMapping ? isDistributive((type as StringMappingType).type) :
- false;
- }
- }
-
function getLiteralTypeFromPropertyName(name: PropertyName | JsxAttributeName) {
if (isPrivateIdentifier(name)) {
return neverType;
@@ -18350,7 +18337,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function shouldDeferIndexType(type: Type, indexFlags = IndexFlags.None) {
return !!(type.flags & TypeFlags.InstantiableNonPrimitive ||
isGenericTupleType(type) ||
- isGenericMappedType(type) && (!hasDistributiveNameType(type) || getMappedTypeNameTypeKind(type) === MappedTypeNameTypeKind.Remapping) ||
+ isGenericMappedType(type) && getNameTypeFromMappedType(type) ||
type.flags & TypeFlags.Union && !(indexFlags & IndexFlags.NoReducibleCheck) && isGenericReducibleType(type) ||
type.flags & TypeFlags.Intersection && maybeTypeOfKind(type, TypeFlags.Instantiable) && some((type as IntersectionType).types, isEmptyAnonymousObjectType));
}
@@ -18871,6 +18858,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function getSimplifiedType(type: Type, writing: boolean): Type {
return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(type as IndexedAccessType, writing) :
type.flags & TypeFlags.Conditional ? getSimplifiedConditionalType(type as ConditionalType, writing) :
+ type.flags & TypeFlags.Index ? getSimplifiedIndexType(type as IndexType) :
type;
}
@@ -18970,6 +18958,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return type;
}
+ function getSimplifiedIndexType(type: IndexType) {
+ if (isGenericMappedType(type.type) && getNameTypeFromMappedType(type.type) && !isMappedTypeWithKeyofConstraintDeclaration(type.type)) {
+ return getIndexTypeForMappedType(type.type, IndexFlags.None);
+ }
+ return type;
+ }
+
/**
* Invokes union simplification logic to determine if an intersection is considered empty as a union constituent
*/
@@ -42086,12 +42081,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// Check if the index type is assignable to 'keyof T' for the object type.
const objectType = (type as IndexedAccessType).objectType;
const indexType = (type as IndexedAccessType).indexType;
- // skip index type deferral on remapping mapped types
- const objectIndexType = isGenericMappedType(objectType) && getMappedTypeNameTypeKind(objectType) === MappedTypeNameTypeKind.Remapping
- ? getIndexTypeForMappedType(objectType, IndexFlags.None)
- : getIndexType(objectType, IndexFlags.None);
const hasNumberIndexInfo = !!getIndexInfoOfType(objectType, numberType);
- if (everyType(indexType, t => isTypeAssignableTo(t, objectIndexType) || hasNumberIndexInfo && isApplicableIndexType(t, numberType))) {
+ if (everyType(indexType, t => isTypeAssignableTo(t, getIndexType(objectType, IndexFlags.None)) || hasNumberIndexInfo && isApplicableIndexType(t, numberType))) {
if (
accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) &&
getObjectFlags(objectType) & ObjectFlags.Mapped && getMappedTypeModifiers(objectType as MappedType) & MappedTypeModifiers.IncludeReadonly
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 9b4915f1cc293..11e7c67402816 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -6340,7 +6340,7 @@ export const enum TypeFlags {
/** @internal */
ObjectFlagsType = Any | Nullable | Never | Object | Union | Intersection,
/** @internal */
- Simplifiable = IndexedAccess | Conditional,
+ Simplifiable = IndexedAccess | Conditional | Index,
/** @internal */
Singleton = Any | Unknown | String | Number | Boolean | BigInt | ESSymbol | Void | Undefined | Null | Never | NonPrimitive,
// 'Narrowable' types are types where narrowing actually narrows.
diff --git a/tests/baselines/reference/keyRemappingKeyofResult.errors.txt b/tests/baselines/reference/keyRemappingKeyofResult.errors.txt
new file mode 100644
index 0000000000000..c92c299465e8b
--- /dev/null
+++ b/tests/baselines/reference/keyRemappingKeyofResult.errors.txt
@@ -0,0 +1,80 @@
+keyRemappingKeyofResult.ts(69,5): error TS2322: Type 'string' is not assignable to type 'keyof Remapped'.
+ Type '"whatever"' is not assignable to type 'unique symbol | "str" | DistributiveNonIndex'.
+
+
+==== keyRemappingKeyofResult.ts (1 errors) ====
+ const sym = Symbol("")
+ type Orig = { [k: string]: any, str: any, [sym]: any }
+
+ type Okay = Exclude
+ // type Okay = string | number | typeof sym
+
+ type Remapped = { [K in keyof Orig as {} extends Record ? never : K]: any }
+ /* type Remapped = {
+ str: any;
+ [sym]: any;
+ } */
+ // no string index signature, right?
+
+ type Oops = Exclude
+ declare let x: Oops;
+ x = sym;
+ x = "str";
+ // type Oops = typeof sym <-- what happened to "str"?
+
+ // equivalently, with an unresolved generic (no `exclude` shenanigans, since conditions won't execute):
+ function f() {
+ type Orig = { [k: string]: any, str: any, [sym]: any } & T;
+
+ type Okay = keyof Orig;
+ let a: Okay;
+ a = "str";
+ a = sym;
+ a = "whatever";
+ // type Okay = string | number | typeof sym
+
+ type Remapped = { [K in keyof Orig as {} extends Record ? never : K]: any }
+ /* type Remapped = {
+ str: any;
+ [sym]: any;
+ } */
+ // no string index signature, right?
+
+ type Oops = keyof Remapped;
+ let x: Oops;
+ x = sym;
+ x = "str";
+ }
+
+ // and another generic case with a _distributive_ mapping, to trigger a different branch in `getIndexType`
+ function g() {
+ type Orig = { [k: string]: any, str: any, [sym]: any } & T;
+
+ type Okay = keyof Orig;
+ let a: Okay;
+ a = "str";
+ a = sym;
+ a = "whatever";
+ // type Okay = string | number | typeof sym
+
+ type NonIndex = {} extends Record ? never : T;
+ type DistributiveNonIndex = T extends unknown ? NonIndex : never;
+
+ type Remapped = { [K in keyof Orig as DistributiveNonIndex]: any }
+ /* type Remapped = {
+ str: any;
+ [sym]: any;
+ } */
+ // no string index signature, right?
+
+ type Oops = keyof Remapped;
+ let x: Oops;
+ x = sym;
+ x = "str";
+ x = "whatever"; // error
+ ~
+!!! error TS2322: Type 'string' is not assignable to type 'keyof Remapped'.
+!!! error TS2322: Type '"whatever"' is not assignable to type 'unique symbol | "str" | DistributiveNonIndex'.
+ }
+
+ export {};
\ No newline at end of file
diff --git a/tests/baselines/reference/keyRemappingKeyofResult.js b/tests/baselines/reference/keyRemappingKeyofResult.js
index 3b51e02dc735e..9e4d1ea336c3f 100644
--- a/tests/baselines/reference/keyRemappingKeyofResult.js
+++ b/tests/baselines/reference/keyRemappingKeyofResult.js
@@ -69,6 +69,7 @@ function g() {
let x: Oops;
x = sym;
x = "str";
+ x = "whatever"; // error
}
export {};
@@ -97,5 +98,6 @@ function g() {
let x;
x = sym;
x = "str";
+ x = "whatever"; // error
}
export {};
diff --git a/tests/baselines/reference/keyRemappingKeyofResult.symbols b/tests/baselines/reference/keyRemappingKeyofResult.symbols
index 722c000f268fa..3be5cf2460e84 100644
--- a/tests/baselines/reference/keyRemappingKeyofResult.symbols
+++ b/tests/baselines/reference/keyRemappingKeyofResult.symbols
@@ -190,6 +190,9 @@ function g() {
x = "str";
>x : Symbol(x, Decl(keyRemappingKeyofResult.ts, 65, 7))
+
+ x = "whatever"; // error
+>x : Symbol(x, Decl(keyRemappingKeyofResult.ts, 65, 7))
}
export {};
diff --git a/tests/baselines/reference/keyRemappingKeyofResult.types b/tests/baselines/reference/keyRemappingKeyofResult.types
index 50949e385daa2..ce371fb30e5de 100644
--- a/tests/baselines/reference/keyRemappingKeyofResult.types
+++ b/tests/baselines/reference/keyRemappingKeyofResult.types
@@ -17,7 +17,9 @@ type Orig = { [k: string]: any, str: any, [sym]: any }
>k : string
> : ^^^^^^
>str : any
+> : ^^^
>[sym] : any
+> : ^^^
>sym : unique symbol
> : ^^^^^^^^^^^^^
@@ -74,7 +76,9 @@ function f() {
>k : string
> : ^^^^^^
>str : any
+> : ^^^
>[sym] : any
+> : ^^^
>sym : unique symbol
> : ^^^^^^^^^^^^^
@@ -158,7 +162,9 @@ function g() {
>k : string
> : ^^^^^^
>str : any
+> : ^^^
>[sym] : any
+> : ^^^
>sym : unique symbol
> : ^^^^^^^^^^^^^
@@ -237,6 +243,14 @@ function g() {
> : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>"str" : "str"
> : ^^^^^
+
+ x = "whatever"; // error
+>x = "whatever" : "whatever"
+> : ^^^^^^^^^^
+>x : keyof { [K in keyof ({ [k: string]: any; str: any; [sym]: any; } & T) as K extends unknown ? {} extends Record ? never : K : never]: any; }
+> : ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>"whatever" : "whatever"
+> : ^^^^^^^^^^
}
export {};
diff --git a/tests/baselines/reference/keyRemappingKeyofResult2.symbols b/tests/baselines/reference/keyRemappingKeyofResult2.symbols
new file mode 100644
index 0000000000000..b3fd16667f89f
--- /dev/null
+++ b/tests/baselines/reference/keyRemappingKeyofResult2.symbols
@@ -0,0 +1,84 @@
+//// [tests/cases/compiler/keyRemappingKeyofResult2.ts] ////
+
+=== keyRemappingKeyofResult2.ts ===
+// https://github.com/microsoft/TypeScript/issues/56239
+
+type Values = T[keyof T];
+>Values : Symbol(Values, Decl(keyRemappingKeyofResult2.ts, 0, 0))
+>T : Symbol(T, Decl(keyRemappingKeyofResult2.ts, 2, 12))
+>T : Symbol(T, Decl(keyRemappingKeyofResult2.ts, 2, 12))
+>T : Symbol(T, Decl(keyRemappingKeyofResult2.ts, 2, 12))
+
+type ProvidedActor = {
+>ProvidedActor : Symbol(ProvidedActor, Decl(keyRemappingKeyofResult2.ts, 2, 28))
+
+ src: string;
+>src : Symbol(src, Decl(keyRemappingKeyofResult2.ts, 4, 22))
+
+ logic: unknown;
+>logic : Symbol(logic, Decl(keyRemappingKeyofResult2.ts, 5, 14))
+
+};
+
+interface StateMachineConfig {
+>StateMachineConfig : Symbol(StateMachineConfig, Decl(keyRemappingKeyofResult2.ts, 7, 2))
+>TActors : Symbol(TActors, Decl(keyRemappingKeyofResult2.ts, 9, 29))
+>ProvidedActor : Symbol(ProvidedActor, Decl(keyRemappingKeyofResult2.ts, 2, 28))
+
+ invoke: {
+>invoke : Symbol(StateMachineConfig.invoke, Decl(keyRemappingKeyofResult2.ts, 9, 61))
+
+ src: TActors["src"];
+>src : Symbol(src, Decl(keyRemappingKeyofResult2.ts, 10, 11))
+>TActors : Symbol(TActors, Decl(keyRemappingKeyofResult2.ts, 9, 29))
+
+ };
+}
+
+declare function setup>(_: {
+>setup : Symbol(setup, Decl(keyRemappingKeyofResult2.ts, 13, 1))
+>TActors : Symbol(TActors, Decl(keyRemappingKeyofResult2.ts, 15, 23))
+>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
+>_ : Symbol(_, Decl(keyRemappingKeyofResult2.ts, 15, 64))
+
+ actors: {
+>actors : Symbol(actors, Decl(keyRemappingKeyofResult2.ts, 15, 68))
+
+ [K in keyof TActors]: TActors[K];
+>K : Symbol(K, Decl(keyRemappingKeyofResult2.ts, 17, 5))
+>TActors : Symbol(TActors, Decl(keyRemappingKeyofResult2.ts, 15, 23))
+>TActors : Symbol(TActors, Decl(keyRemappingKeyofResult2.ts, 15, 23))
+>K : Symbol(K, Decl(keyRemappingKeyofResult2.ts, 17, 5))
+
+ };
+}): {
+ createMachine: (
+>createMachine : Symbol(createMachine, Decl(keyRemappingKeyofResult2.ts, 19, 5))
+
+ config: StateMachineConfig<
+>config : Symbol(config, Decl(keyRemappingKeyofResult2.ts, 20, 18))
+>StateMachineConfig : Symbol(StateMachineConfig, Decl(keyRemappingKeyofResult2.ts, 7, 2))
+
+ Values<{
+>Values : Symbol(Values, Decl(keyRemappingKeyofResult2.ts, 0, 0))
+
+ [K in keyof TActors as K & string]: {
+>K : Symbol(K, Decl(keyRemappingKeyofResult2.ts, 23, 9))
+>TActors : Symbol(TActors, Decl(keyRemappingKeyofResult2.ts, 15, 23))
+>K : Symbol(K, Decl(keyRemappingKeyofResult2.ts, 23, 9))
+
+ src: K;
+>src : Symbol(src, Decl(keyRemappingKeyofResult2.ts, 23, 45))
+>K : Symbol(K, Decl(keyRemappingKeyofResult2.ts, 23, 9))
+
+ logic: TActors[K];
+>logic : Symbol(logic, Decl(keyRemappingKeyofResult2.ts, 24, 17))
+>TActors : Symbol(TActors, Decl(keyRemappingKeyofResult2.ts, 15, 23))
+>K : Symbol(K, Decl(keyRemappingKeyofResult2.ts, 23, 9))
+
+ };
+ }>
+ >,
+ ) => void;
+};
+
diff --git a/tests/baselines/reference/keyRemappingKeyofResult2.types b/tests/baselines/reference/keyRemappingKeyofResult2.types
new file mode 100644
index 0000000000000..b65ebbf5b6a0e
--- /dev/null
+++ b/tests/baselines/reference/keyRemappingKeyofResult2.types
@@ -0,0 +1,72 @@
+//// [tests/cases/compiler/keyRemappingKeyofResult2.ts] ////
+
+=== keyRemappingKeyofResult2.ts ===
+// https://github.com/microsoft/TypeScript/issues/56239
+
+type Values = T[keyof T];
+>Values : Values
+> : ^^^^^^^^^
+
+type ProvidedActor = {
+>ProvidedActor : ProvidedActor
+> : ^^^^^^^^^^^^^
+
+ src: string;
+>src : string
+> : ^^^^^^
+
+ logic: unknown;
+>logic : unknown
+> : ^^^^^^^
+
+};
+
+interface StateMachineConfig {
+ invoke: {
+>invoke : { src: TActors["src"]; }
+> : ^^^^^^^ ^^^
+
+ src: TActors["src"];
+>src : TActors["src"]
+> : ^^^^^^^^^^^^^^
+
+ };
+}
+
+declare function setup>(_: {
+>setup : >(_: { actors: { [K in keyof TActors]: TActors[K]; }; }) => { createMachine: (config: StateMachineConfig>) => void; }
+> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
+>_ : { actors: { [K in keyof TActors]: TActors[K]; }; }
+> : ^^^^^^^^^^ ^^^
+
+ actors: {
+>actors : { [K in keyof TActors]: TActors[K]; }
+> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ [K in keyof TActors]: TActors[K];
+ };
+}): {
+ createMachine: (
+>createMachine : (config: StateMachineConfig>) => void
+> : ^ ^^ ^^^^^
+
+ config: StateMachineConfig<
+>config : StateMachineConfig>
+> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^
+
+ Values<{
+ [K in keyof TActors as K & string]: {
+ src: K;
+>src : K
+> : ^
+
+ logic: TActors[K];
+>logic : TActors[K]
+> : ^^^^^^^^^^
+
+ };
+ }>
+ >,
+ ) => void;
+};
+
diff --git a/tests/baselines/reference/mappedTypeAsClauseRecursiveNoCrash1.symbols b/tests/baselines/reference/mappedTypeAsClauseRecursiveNoCrash1.symbols
new file mode 100644
index 0000000000000..2aa6ec2b1a5bd
--- /dev/null
+++ b/tests/baselines/reference/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/tests/baselines/reference/mappedTypeAsClauseRecursiveNoCrash1.types b/tests/baselines/reference/mappedTypeAsClauseRecursiveNoCrash1.types
new file mode 100644
index 0000000000000..945943cb99f36
--- /dev/null
+++ b/tests/baselines/reference/mappedTypeAsClauseRecursiveNoCrash1.types
@@ -0,0 +1,89 @@
+//// [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 : "id" | "name" | "address.address" | "address.postCode" | "address.description"
+> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+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 : "id" | "name" | "address.address" | "address.postCode" | "address.description"
+> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
diff --git a/tests/baselines/reference/mappedTypeConstraints2.errors.txt b/tests/baselines/reference/mappedTypeConstraints2.errors.txt
index 0d3b3244c9e66..a94ee5f734764 100644
--- a/tests/baselines/reference/mappedTypeConstraints2.errors.txt
+++ b/tests/baselines/reference/mappedTypeConstraints2.errors.txt
@@ -4,11 +4,11 @@ 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(51,57): error TS2322: Type 'Foo[`get${T}`]' is not assignable to type 'T'.
+ Type 'Mapped6[`_${K}`]' 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(82,9): error TS2322: Type 'ObjectWithUnderscoredKeys[`_${K}`]' is not assignable to type 'true'.
+mappedTypeConstraints2.ts(90,9): error TS2322: Type 'ObjectWithUnderscoredKeys[`_${K}`]' is not assignable to type 'true'.
Type 'ObjectWithUnderscoredKeys[`_${string}`]' is not assignable to type 'true'.
@@ -64,8 +64,16 @@ mappedTypeConstraints2.ts(82,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[`_${K}`]' is not assignable to type '`_${string}`'.
+!!! error TS2322: Type 'Mapped6[`_${string}`]' is not assignable to type '`_${string}`'.
+ }
+
+ type Mapped7 = {
+ [P in K as [P] extends [`_${string}`] ? P : never]: P;
+ };
+
+ function f7(obj: Mapped7, key: keyof Mapped7) {
+ let s: `_${string}` = obj[key];
}
// Repro from #47794
diff --git a/tests/baselines/reference/mappedTypeConstraints2.js b/tests/baselines/reference/mappedTypeConstraints2.js
index df4e0eb12d429..4c19254560680 100644
--- a/tests/baselines/reference/mappedTypeConstraints2.js
+++ b/tests/baselines/reference/mappedTypeConstraints2.js
@@ -45,6 +45,14 @@ function f6(obj: Mapped6, key: keyof Mapped6) {
let s: `_${string}` = obj[key]; // Error
}
+type Mapped7 = {
+ [P in K as [P] extends [`_${string}`] ? P : never]: P;
+};
+
+function f7(obj: Mapped7, key: keyof Mapped7) {
+ let s: `_${string}` = obj[key];
+}
+
// Repro from #47794
type Foo = {
@@ -106,6 +114,9 @@ function f5(obj, key) {
function f6(obj, key) {
let s = obj[key]; // Error
}
+function f7(obj, key) {
+ let s = obj[key];
+}
const get = (t, foo) => foo[`get${t}`]; // Type 'Foo[`get${T}`]' is not assignable to type 'T'
function validate(obj, bounds) {
for (const [key, val] of Object.entries(obj)) {
@@ -154,6 +165,10 @@ type Mapped6 = {
[P in K as `_${P}`]: P;
};
declare function f6(obj: Mapped6, key: keyof Mapped6): void;
+type Mapped7 = {
+ [P in K as [P] extends [`_${string}`] ? P : never]: P;
+};
+declare function f7(obj: Mapped7, key: keyof Mapped7): void;
type Foo = {
[RemappedT in T as `get${RemappedT}`]: RemappedT;
};
diff --git a/tests/baselines/reference/mappedTypeConstraints2.symbols b/tests/baselines/reference/mappedTypeConstraints2.symbols
index 74a16d5ac1446..1cf513e4e63fc 100644
--- a/tests/baselines/reference/mappedTypeConstraints2.symbols
+++ b/tests/baselines/reference/mappedTypeConstraints2.symbols
@@ -166,94 +166,123 @@ function f6(obj: Mapped6, key: keyof Mapped6) {
>key : Symbol(key, Decl(mappedTypeConstraints2.ts, 40, 46))
}
+type Mapped7 = {
+>Mapped7 : Symbol(Mapped7, Decl(mappedTypeConstraints2.ts, 42, 1))
+>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 44, 13))
+
+ [P in K as [P] extends [`_${string}`] ? P : never]: P;
+>P : Symbol(P, Decl(mappedTypeConstraints2.ts, 45, 3))
+>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 44, 13))
+>P : Symbol(P, Decl(mappedTypeConstraints2.ts, 45, 3))
+>P : Symbol(P, Decl(mappedTypeConstraints2.ts, 45, 3))
+>P : Symbol(P, Decl(mappedTypeConstraints2.ts, 45, 3))
+
+};
+
+function f7(obj: Mapped7, key: keyof Mapped7) {
+>f7 : Symbol(f7, Decl(mappedTypeConstraints2.ts, 46, 2))
+>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 48, 12))
+>obj : Symbol(obj, Decl(mappedTypeConstraints2.ts, 48, 30))
+>Mapped7 : Symbol(Mapped7, Decl(mappedTypeConstraints2.ts, 42, 1))
+>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 48, 12))
+>key : Symbol(key, Decl(mappedTypeConstraints2.ts, 48, 46))
+>Mapped7 : Symbol(Mapped7, Decl(mappedTypeConstraints2.ts, 42, 1))
+>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 48, 12))
+
+ let s: `_${string}` = obj[key];
+>s : Symbol(s, Decl(mappedTypeConstraints2.ts, 49, 5))
+>obj : Symbol(obj, Decl(mappedTypeConstraints2.ts, 48, 30))
+>key : Symbol(key, Decl(mappedTypeConstraints2.ts, 48, 46))
+}
+
// Repro from #47794
type Foo = {
->Foo : Symbol(Foo, Decl(mappedTypeConstraints2.ts, 42, 1))
->T : Symbol(T, Decl(mappedTypeConstraints2.ts, 46, 9))
+>Foo : Symbol(Foo, Decl(mappedTypeConstraints2.ts, 50, 1))
+>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 54, 9))
[RemappedT in T as `get${RemappedT}`]: RemappedT;
->RemappedT : Symbol(RemappedT, Decl(mappedTypeConstraints2.ts, 47, 5))
->T : Symbol(T, Decl(mappedTypeConstraints2.ts, 46, 9))
->RemappedT : Symbol(RemappedT, Decl(mappedTypeConstraints2.ts, 47, 5))
->RemappedT : Symbol(RemappedT, Decl(mappedTypeConstraints2.ts, 47, 5))
+>RemappedT : Symbol(RemappedT, Decl(mappedTypeConstraints2.ts, 55, 5))
+>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 54, 9))
+>RemappedT : Symbol(RemappedT, Decl(mappedTypeConstraints2.ts, 55, 5))
+>RemappedT : Symbol(RemappedT, Decl(mappedTypeConstraints2.ts, 55, 5))
};
const get = (t: T, foo: Foo): T => foo[`get${t}`]; // Type 'Foo[`get${T}`]' is not assignable to type 'T'
->get : Symbol(get, Decl(mappedTypeConstraints2.ts, 50, 5))
->T : Symbol(T, Decl(mappedTypeConstraints2.ts, 50, 13))
->t : Symbol(t, Decl(mappedTypeConstraints2.ts, 50, 31))
->T : Symbol(T, Decl(mappedTypeConstraints2.ts, 50, 13))
->foo : Symbol(foo, Decl(mappedTypeConstraints2.ts, 50, 36))
->Foo : Symbol(Foo, Decl(mappedTypeConstraints2.ts, 42, 1))
->T : Symbol(T, Decl(mappedTypeConstraints2.ts, 50, 13))
->T : Symbol(T, Decl(mappedTypeConstraints2.ts, 50, 13))
->foo : Symbol(foo, Decl(mappedTypeConstraints2.ts, 50, 36))
->t : Symbol(t, Decl(mappedTypeConstraints2.ts, 50, 31))
+>get : Symbol(get, Decl(mappedTypeConstraints2.ts, 58, 5))
+>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 58, 13))
+>t : Symbol(t, Decl(mappedTypeConstraints2.ts, 58, 31))
+>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 58, 13))
+>foo : Symbol(foo, Decl(mappedTypeConstraints2.ts, 58, 36))
+>Foo : Symbol(Foo, Decl(mappedTypeConstraints2.ts, 50, 1))
+>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 58, 13))
+>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 58, 13))
+>foo : Symbol(foo, Decl(mappedTypeConstraints2.ts, 58, 36))
+>t : Symbol(t, Decl(mappedTypeConstraints2.ts, 58, 31))
// Repro from #48626
interface Bounds {
->Bounds : Symbol(Bounds, Decl(mappedTypeConstraints2.ts, 50, 71))
+>Bounds : Symbol(Bounds, Decl(mappedTypeConstraints2.ts, 58, 71))
min: number;
->min : Symbol(Bounds.min, Decl(mappedTypeConstraints2.ts, 54, 18))
+>min : Symbol(Bounds.min, Decl(mappedTypeConstraints2.ts, 62, 18))
max: number;
->max : Symbol(Bounds.max, Decl(mappedTypeConstraints2.ts, 55, 16))
+>max : Symbol(Bounds.max, Decl(mappedTypeConstraints2.ts, 63, 16))
}
type NumericBoundsOf = {
->NumericBoundsOf : Symbol(NumericBoundsOf, Decl(mappedTypeConstraints2.ts, 57, 1))
->T : Symbol(T, Decl(mappedTypeConstraints2.ts, 59, 21))
+>NumericBoundsOf : Symbol(NumericBoundsOf, Decl(mappedTypeConstraints2.ts, 65, 1))
+>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 67, 21))
[K in keyof T as T[K] extends number | undefined ? K : never]: Bounds;
->K : Symbol(K, Decl(mappedTypeConstraints2.ts, 60, 5))
->T : Symbol(T, Decl(mappedTypeConstraints2.ts, 59, 21))
->T : Symbol(T, Decl(mappedTypeConstraints2.ts, 59, 21))
->K : Symbol(K, Decl(mappedTypeConstraints2.ts, 60, 5))
->K : Symbol(K, Decl(mappedTypeConstraints2.ts, 60, 5))
->Bounds : Symbol(Bounds, Decl(mappedTypeConstraints2.ts, 50, 71))
+>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 68, 5))
+>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 67, 21))
+>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 67, 21))
+>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 68, 5))
+>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 68, 5))
+>Bounds : Symbol(Bounds, Decl(mappedTypeConstraints2.ts, 58, 71))
}
function validate(obj: T, bounds: NumericBoundsOf) {
->validate : Symbol(validate, Decl(mappedTypeConstraints2.ts, 61, 1))
->T : Symbol(T, Decl(mappedTypeConstraints2.ts, 63, 18))
->obj : Symbol(obj, Decl(mappedTypeConstraints2.ts, 63, 36))
->T : Symbol(T, Decl(mappedTypeConstraints2.ts, 63, 18))
->bounds : Symbol(bounds, Decl(mappedTypeConstraints2.ts, 63, 43))
->NumericBoundsOf : Symbol(NumericBoundsOf, Decl(mappedTypeConstraints2.ts, 57, 1))
->T : Symbol(T, Decl(mappedTypeConstraints2.ts, 63, 18))
+>validate : Symbol(validate, Decl(mappedTypeConstraints2.ts, 69, 1))
+>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 71, 18))
+>obj : Symbol(obj, Decl(mappedTypeConstraints2.ts, 71, 36))
+>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 71, 18))
+>bounds : Symbol(bounds, Decl(mappedTypeConstraints2.ts, 71, 43))
+>NumericBoundsOf : Symbol(NumericBoundsOf, Decl(mappedTypeConstraints2.ts, 65, 1))
+>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 71, 18))
for (const [key, val] of Object.entries(obj)) {
->key : Symbol(key, Decl(mappedTypeConstraints2.ts, 64, 16))
->val : Symbol(val, Decl(mappedTypeConstraints2.ts, 64, 20))
+>key : Symbol(key, Decl(mappedTypeConstraints2.ts, 72, 16))
+>val : Symbol(val, Decl(mappedTypeConstraints2.ts, 72, 20))
>Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --))
->obj : Symbol(obj, Decl(mappedTypeConstraints2.ts, 63, 36))
+>obj : Symbol(obj, Decl(mappedTypeConstraints2.ts, 71, 36))
const boundsForKey = bounds[key as keyof NumericBoundsOf];
->boundsForKey : Symbol(boundsForKey, Decl(mappedTypeConstraints2.ts, 65, 13))
->bounds : Symbol(bounds, Decl(mappedTypeConstraints2.ts, 63, 43))
->key : Symbol(key, Decl(mappedTypeConstraints2.ts, 64, 16))
->NumericBoundsOf : Symbol(NumericBoundsOf, Decl(mappedTypeConstraints2.ts, 57, 1))
->T : Symbol(T, Decl(mappedTypeConstraints2.ts, 63, 18))
+>boundsForKey : Symbol(boundsForKey, Decl(mappedTypeConstraints2.ts, 73, 13))
+>bounds : Symbol(bounds, Decl(mappedTypeConstraints2.ts, 71, 43))
+>key : Symbol(key, Decl(mappedTypeConstraints2.ts, 72, 16))
+>NumericBoundsOf : Symbol(NumericBoundsOf, Decl(mappedTypeConstraints2.ts, 65, 1))
+>T : Symbol(T, Decl(mappedTypeConstraints2.ts, 71, 18))
if (boundsForKey) {
->boundsForKey : Symbol(boundsForKey, Decl(mappedTypeConstraints2.ts, 65, 13))
+>boundsForKey : Symbol(boundsForKey, Decl(mappedTypeConstraints2.ts, 73, 13))
const { min, max } = boundsForKey;
->min : Symbol(min, Decl(mappedTypeConstraints2.ts, 67, 19))
->max : Symbol(max, Decl(mappedTypeConstraints2.ts, 67, 24))
->boundsForKey : Symbol(boundsForKey, Decl(mappedTypeConstraints2.ts, 65, 13))
+>min : Symbol(min, Decl(mappedTypeConstraints2.ts, 75, 19))
+>max : Symbol(max, Decl(mappedTypeConstraints2.ts, 75, 24))
+>boundsForKey : Symbol(boundsForKey, Decl(mappedTypeConstraints2.ts, 73, 13))
if (min > val || max < val) return false;
->min : Symbol(min, Decl(mappedTypeConstraints2.ts, 67, 19))
->val : Symbol(val, Decl(mappedTypeConstraints2.ts, 64, 20))
->max : Symbol(max, Decl(mappedTypeConstraints2.ts, 67, 24))
->val : Symbol(val, Decl(mappedTypeConstraints2.ts, 64, 20))
+>min : Symbol(min, Decl(mappedTypeConstraints2.ts, 75, 19))
+>val : Symbol(val, Decl(mappedTypeConstraints2.ts, 72, 20))
+>max : Symbol(max, Decl(mappedTypeConstraints2.ts, 75, 24))
+>val : Symbol(val, Decl(mappedTypeConstraints2.ts, 72, 20))
}
}
return true;
@@ -262,28 +291,28 @@ function validate(obj: T, bounds: NumericBoundsOf) {
// repro from #50030
type ObjectWithUnderscoredKeys = {
->ObjectWithUnderscoredKeys : Symbol(ObjectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 72, 1))
->K : Symbol(K, Decl(mappedTypeConstraints2.ts, 76, 31))
+>ObjectWithUnderscoredKeys : Symbol(ObjectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 80, 1))
+>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 84, 31))
[k in K as `_${k}`]: true;
->k : Symbol(k, Decl(mappedTypeConstraints2.ts, 77, 5))
->K : Symbol(K, Decl(mappedTypeConstraints2.ts, 76, 31))
->k : Symbol(k, Decl(mappedTypeConstraints2.ts, 77, 5))
+>k : Symbol(k, Decl(mappedTypeConstraints2.ts, 85, 5))
+>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 84, 31))
+>k : Symbol(k, Decl(mappedTypeConstraints2.ts, 85, 5))
};
function genericTest(objectWithUnderscoredKeys: ObjectWithUnderscoredKeys, key: K) {
->genericTest : Symbol(genericTest, Decl(mappedTypeConstraints2.ts, 78, 2))
->K : Symbol(K, Decl(mappedTypeConstraints2.ts, 80, 21))
->objectWithUnderscoredKeys : Symbol(objectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 80, 39))
->ObjectWithUnderscoredKeys : Symbol(ObjectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 72, 1))
->K : Symbol(K, Decl(mappedTypeConstraints2.ts, 80, 21))
->key : Symbol(key, Decl(mappedTypeConstraints2.ts, 80, 95))
->K : Symbol(K, Decl(mappedTypeConstraints2.ts, 80, 21))
+>genericTest : Symbol(genericTest, Decl(mappedTypeConstraints2.ts, 86, 2))
+>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 88, 21))
+>objectWithUnderscoredKeys : Symbol(objectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 88, 39))
+>ObjectWithUnderscoredKeys : Symbol(ObjectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 80, 1))
+>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 88, 21))
+>key : Symbol(key, Decl(mappedTypeConstraints2.ts, 88, 95))
+>K : Symbol(K, Decl(mappedTypeConstraints2.ts, 88, 21))
const shouldBeTrue: true = objectWithUnderscoredKeys[`_${key}`]; // assignability fails here, but ideally should not
->shouldBeTrue : Symbol(shouldBeTrue, Decl(mappedTypeConstraints2.ts, 81, 7))
->objectWithUnderscoredKeys : Symbol(objectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 80, 39))
->key : Symbol(key, Decl(mappedTypeConstraints2.ts, 80, 95))
+>shouldBeTrue : Symbol(shouldBeTrue, Decl(mappedTypeConstraints2.ts, 89, 7))
+>objectWithUnderscoredKeys : Symbol(objectWithUnderscoredKeys, Decl(mappedTypeConstraints2.ts, 88, 39))
+>key : Symbol(key, Decl(mappedTypeConstraints2.ts, 88, 95))
}
diff --git a/tests/baselines/reference/mappedTypeConstraints2.types b/tests/baselines/reference/mappedTypeConstraints2.types
index b975d98fa9293..3c1d30fa74117 100644
--- a/tests/baselines/reference/mappedTypeConstraints2.types
+++ b/tests/baselines/reference/mappedTypeConstraints2.types
@@ -120,18 +120,18 @@ function f5(obj: Mapped5, key: keyof Mapped5) {
> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^^^^^
>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
@@ -162,6 +162,32 @@ function f6(obj: Mapped6, key: keyof Mapped6) {
> : ^^^^^^^^^^^^^^^^
}
+type Mapped7 = {
+>Mapped7 : Mapped7
+> : ^^^^^^^^^^
+
+ [P in K as [P] extends [`_${string}`] ? P : never]: P;
+};
+
+function f7(obj: Mapped7, key: keyof Mapped7) {
+>f7 : (obj: Mapped7, key: keyof Mapped7) => void
+> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^^^^^
+>obj : Mapped7
+> : ^^^^^^^^^^
+>key : keyof Mapped7
+> : ^^^^^^^^^^^^^^^^
+
+ let s: `_${string}` = obj[key];
+>s : `_${string}`
+> : ^^^^^^^^^^^^
+>obj[key] : Mapped7[keyof Mapped7]
+> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+>obj : Mapped7
+> : ^^^^^^^^^^
+>key : keyof Mapped7
+> : ^^^^^^^^^^^^^^^^
+}
+
// Repro from #47794
type Foo = {
diff --git a/tests/baselines/reference/substitutionTypeForIndexedAccessType2.js b/tests/baselines/reference/substitutionTypeForIndexedAccessType2.js
index a73af0b42ab1f..ff8728571aa47 100644
--- a/tests/baselines/reference/substitutionTypeForIndexedAccessType2.js
+++ b/tests/baselines/reference/substitutionTypeForIndexedAccessType2.js
@@ -10,9 +10,6 @@ type Str = T
type Bar =
T extends Foo
? T['foo'] extends string
- // Type 'T["foo"]' does not satisfy the constraint 'string'.
- // Type 'string | undefined' is not assignable to type 'string'.
- // Type 'undefined' is not assignable to type 'string'.(2344)
? Str
: never
: never
diff --git a/tests/baselines/reference/substitutionTypeForIndexedAccessType2.symbols b/tests/baselines/reference/substitutionTypeForIndexedAccessType2.symbols
index fc2995682b466..f9e6e9a2ce0fa 100644
--- a/tests/baselines/reference/substitutionTypeForIndexedAccessType2.symbols
+++ b/tests/baselines/reference/substitutionTypeForIndexedAccessType2.symbols
@@ -24,9 +24,6 @@ type Bar =
? T['foo'] extends string
>T : Symbol(T, Decl(substitutionTypeForIndexedAccessType2.ts, 6, 9))
- // Type 'T["foo"]' does not satisfy the constraint 'string'.
- // Type 'string | undefined' is not assignable to type 'string'.
- // Type 'undefined' is not assignable to type 'string'.(2344)
? Str
>Str : Symbol(Str, Decl(substitutionTypeForIndexedAccessType2.ts, 2, 1))
>T : Symbol(T, Decl(substitutionTypeForIndexedAccessType2.ts, 6, 9))
diff --git a/tests/baselines/reference/substitutionTypeForIndexedAccessType2.types b/tests/baselines/reference/substitutionTypeForIndexedAccessType2.types
index 4567d59489a15..d303e54936727 100644
--- a/tests/baselines/reference/substitutionTypeForIndexedAccessType2.types
+++ b/tests/baselines/reference/substitutionTypeForIndexedAccessType2.types
@@ -17,9 +17,6 @@ type Bar =
T extends Foo
? T['foo'] extends string
- // Type 'T["foo"]' does not satisfy the constraint 'string'.
- // Type 'string | undefined' is not assignable to type 'string'.
- // Type 'undefined' is not assignable to type 'string'.(2344)
? Str
: never
: never
diff --git a/tests/cases/compiler/keyRemappingKeyofResult.ts b/tests/cases/compiler/keyRemappingKeyofResult.ts
index fcf3f835ac691..ba0b0939ae3cb 100644
--- a/tests/cases/compiler/keyRemappingKeyofResult.ts
+++ b/tests/cases/compiler/keyRemappingKeyofResult.ts
@@ -67,6 +67,7 @@ function g() {
let x: Oops;
x = sym;
x = "str";
+ x = "whatever"; // error
}
export {};
\ No newline at end of file
diff --git a/tests/cases/compiler/keyRemappingKeyofResult2.ts b/tests/cases/compiler/keyRemappingKeyofResult2.ts
new file mode 100644
index 0000000000000..b77b7af1760a9
--- /dev/null
+++ b/tests/cases/compiler/keyRemappingKeyofResult2.ts
@@ -0,0 +1,34 @@
+// @strict: true
+// @noEmit: true
+
+// 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];
+ };
+ }>
+ >,
+ ) => void;
+};
diff --git a/tests/cases/compiler/substitutionTypeForIndexedAccessType2.ts b/tests/cases/compiler/substitutionTypeForIndexedAccessType2.ts
index c687879efd463..97c0f6f538db5 100644
--- a/tests/cases/compiler/substitutionTypeForIndexedAccessType2.ts
+++ b/tests/cases/compiler/substitutionTypeForIndexedAccessType2.ts
@@ -7,9 +7,6 @@ type Str = T
type Bar =
T extends Foo
? T['foo'] extends string
- // Type 'T["foo"]' does not satisfy the constraint 'string'.
- // Type 'string | undefined' is not assignable to type 'string'.
- // Type 'undefined' is not assignable to type 'string'.(2344)
? Str
: never
: never
\ No newline at end of file
diff --git a/tests/cases/conformance/types/mapped/mappedTypeAsClauseRecursiveNoCrash1.ts b/tests/cases/conformance/types/mapped/mappedTypeAsClauseRecursiveNoCrash1.ts
new file mode 100644
index 0000000000000..e6d25ff1796f8
--- /dev/null
+++ b/tests/cases/conformance/types/mapped/mappedTypeAsClauseRecursiveNoCrash1.ts
@@ -0,0 +1,42 @@
+// @strict: true
+// @noEmit: true
+
+// https://github.com/microsoft/TypeScript/issues/60476
+
+export type FlattenType = {
+ [Key in keyof Source as Key extends string
+ ? Source[Key] extends object
+ ? `${Key}.${keyof FlattenType & string}`
+ : Key
+ : never]-?: Target;
+};
+
+type FieldSelect = {
+ table: string;
+ field: string;
+};
+
+type Address = {
+ postCode: string;
+ description: string;
+ address: string;
+};
+
+type User = {
+ id: number;
+ name: string;
+ address: Address;
+};
+
+type FlattenedUser = FlattenType;
+type FlattenedUserKeys = keyof FlattenType;
+
+export type FlattenTypeKeys = keyof {
+ [Key in keyof Source as Key extends string
+ ? Source[Key] extends object
+ ? `${Key}.${keyof FlattenType & string}`
+ : Key
+ : never]-?: Target;
+};
+
+type FlattenedUserKeys2 = FlattenTypeKeys;
diff --git a/tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts b/tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts
index 250be640f631e..83738d21a5cc1 100644
--- a/tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts
+++ b/tests/cases/conformance/types/mapped/mappedTypeConstraints2.ts
@@ -46,6 +46,14 @@ function f6(obj: Mapped6, key: keyof Mapped6) {
let s: `_${string}` = obj[key]; // Error
}
+type Mapped7 = {
+ [P in K as [P] extends [`_${string}`] ? P : never]: P;
+};
+
+function f7(obj: Mapped7, key: keyof Mapped7) {
+ let s: `_${string}` = obj[key];
+}
+
// Repro from #47794
type Foo = {