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 = {