diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 2a4ead5003a89..3fad1e67792ee 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -22636,7 +22636,7 @@ func (c *Checker) cloneTypeParameter(tp *Type) *Type { } func (c *Checker) getHomomorphicTypeVariable(t *Type) *Type { - constraintType := c.getConstraintTypeFromMappedType(t) + constraintType := c.getActualTypeVariable(c.getConstraintTypeFromMappedType(t)) if constraintType.flags&TypeFlagsIndex != 0 { typeVariable := c.getActualTypeVariable(constraintType.AsIndexType().target) if typeVariable.flags&TypeFlagsTypeParameter != 0 { diff --git a/tsc/testdata/baselines/reference/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.js b/tsc/testdata/baselines/reference/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.js new file mode 100644 index 0000000000000..d7ab0979e3733 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.js @@ -0,0 +1,65 @@ +//// [tests/cases/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.ts] //// + +//// [mappedTypeHomomorphismWithPriorKeyofTChecks1.ts] +type SharedUnionFieldsDeep1 = [T] extends [unknown[]] + ? // Keep only tuple positions guaranteed across the union. + 1 extends T["length"] + ? [T[0]] + : T + : keyof T extends infer CommonKeys + ? ([CommonKeys] extends [never] ? true : false) extends false + ? { + [Key in keyof T]: SharedUnionFieldsDeep1; + } + : {} + : T; + +// distributes +declare const actual1: SharedUnionFieldsDeep1< + { tuple: [number] } | { tuple: [number, string] } +>; + +type SharedUnionFieldsDeep2 = [T] extends [unknown[]] + ? // Keep only tuple positions guaranteed across the union. + 1 extends T["length"] + ? [T[0]] + : T + : keyof T extends infer CommonKeys + ? ([CommonKeys] extends [never] ? true : false) extends false + ? keyof T extends infer Keys extends PropertyKey + ? { + [Key in Keys]: SharedUnionFieldsDeep2; + } + : never + : {} + : T; + +// doesn't distribute +declare const actual2: SharedUnionFieldsDeep2< + { tuple: [number] } | { tuple: [number, string] } +>; + +// `& unknown` used here just to avoid printing type alias at actual3 +type SharedUnionFieldsDeepMappedType3 = { + [Key in K]: SharedUnionFieldsDeep3; +} & unknown; + +type SharedUnionFieldsDeep3 = [T] extends [unknown[]] + ? // Keep only tuple positions guaranteed across the union. + 1 extends T["length"] + ? [T[0]] + : T + : keyof T extends infer CommonKeys + ? ([CommonKeys] extends [never] ? true : false) extends false + ? SharedUnionFieldsDeepMappedType3 + : {} + : T; + +// doesn't distribute either +declare const actual3: SharedUnionFieldsDeep3< + { tuple: [number] } | { tuple: [number, string] } +>; + + +//// [mappedTypeHomomorphismWithPriorKeyofTChecks1.js] +"use strict"; diff --git a/tsc/testdata/baselines/reference/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.symbols b/tsc/testdata/baselines/reference/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.symbols new file mode 100644 index 0000000000000..71f97cca66b93 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.symbols @@ -0,0 +1,158 @@ +//// [tests/cases/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.ts] //// + +=== mappedTypeHomomorphismWithPriorKeyofTChecks1.ts === +type SharedUnionFieldsDeep1 = [T] extends [unknown[]] +>SharedUnionFieldsDeep1 : Symbol(SharedUnionFieldsDeep1, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 0, 0)) +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 0, 28)) +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 0, 28)) + + ? // Keep only tuple positions guaranteed across the union. + 1 extends T["length"] +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 0, 28)) + + ? [T[0]] +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 0, 28)) + + : T +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 0, 28)) + + : keyof T extends infer CommonKeys +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 0, 28)) +>CommonKeys : Symbol(CommonKeys, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 5, 25)) + + ? ([CommonKeys] extends [never] ? true : false) extends false +>CommonKeys : Symbol(CommonKeys, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 5, 25)) + + ? { + [Key in keyof T]: SharedUnionFieldsDeep1; +>Key : Symbol(Key, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 8, 9)) +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 0, 28)) +>SharedUnionFieldsDeep1 : Symbol(SharedUnionFieldsDeep1, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 0, 0)) +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 0, 28)) +>Key : Symbol(Key, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 8, 9)) + } + : {} + : T; +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 0, 28)) + +// distributes +declare const actual1: SharedUnionFieldsDeep1< +>actual1 : Symbol(actual1, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 14, 13)) +>SharedUnionFieldsDeep1 : Symbol(SharedUnionFieldsDeep1, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 0, 0)) + + { tuple: [number] } | { tuple: [number, string] } +>tuple : Symbol(tuple, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 15, 3)) +>tuple : Symbol(tuple, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 15, 25)) + +>; + +type SharedUnionFieldsDeep2 = [T] extends [unknown[]] +>SharedUnionFieldsDeep2 : Symbol(SharedUnionFieldsDeep2, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 16, 2)) +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 18, 28)) +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 18, 28)) + + ? // Keep only tuple positions guaranteed across the union. + 1 extends T["length"] +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 18, 28)) + + ? [T[0]] +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 18, 28)) + + : T +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 18, 28)) + + : keyof T extends infer CommonKeys +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 18, 28)) +>CommonKeys : Symbol(CommonKeys, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 23, 25)) + + ? ([CommonKeys] extends [never] ? true : false) extends false +>CommonKeys : Symbol(CommonKeys, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 23, 25)) + + ? keyof T extends infer Keys extends PropertyKey +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 18, 28)) +>Keys : Symbol(Keys, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 25, 27)) +>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --)) + + ? { + [Key in Keys]: SharedUnionFieldsDeep2; +>Key : Symbol(Key, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 27, 11)) +>Keys : Symbol(Keys, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 25, 27)) +>SharedUnionFieldsDeep2 : Symbol(SharedUnionFieldsDeep2, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 16, 2)) +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 18, 28)) +>Key : Symbol(Key, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 27, 11)) +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 18, 28)) + } + : never + : {} + : T; +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 18, 28)) + +// doesn't distribute +declare const actual2: SharedUnionFieldsDeep2< +>actual2 : Symbol(actual2, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 34, 13)) +>SharedUnionFieldsDeep2 : Symbol(SharedUnionFieldsDeep2, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 16, 2)) + + { tuple: [number] } | { tuple: [number, string] } +>tuple : Symbol(tuple, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 35, 3)) +>tuple : Symbol(tuple, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 35, 25)) + +>; + +// `& unknown` used here just to avoid printing type alias at actual3 +type SharedUnionFieldsDeepMappedType3 = { +>SharedUnionFieldsDeepMappedType3 : Symbol(SharedUnionFieldsDeepMappedType3, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 36, 2)) +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 39, 38)) +>K : Symbol(K, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 39, 40)) +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 39, 38)) +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 39, 38)) + + [Key in K]: SharedUnionFieldsDeep3; +>Key : Symbol(Key, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 40, 3)) +>K : Symbol(K, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 39, 40)) +>SharedUnionFieldsDeep3 : Symbol(SharedUnionFieldsDeep3, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 41, 12)) +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 39, 38)) +>Key : Symbol(Key, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 40, 3)) + +} & unknown; + +type SharedUnionFieldsDeep3 = [T] extends [unknown[]] +>SharedUnionFieldsDeep3 : Symbol(SharedUnionFieldsDeep3, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 41, 12)) +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 43, 28)) +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 43, 28)) + + ? // Keep only tuple positions guaranteed across the union. + 1 extends T["length"] +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 43, 28)) + + ? [T[0]] +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 43, 28)) + + : T +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 43, 28)) + + : keyof T extends infer CommonKeys +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 43, 28)) +>CommonKeys : Symbol(CommonKeys, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 48, 25)) + + ? ([CommonKeys] extends [never] ? true : false) extends false +>CommonKeys : Symbol(CommonKeys, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 48, 25)) + + ? SharedUnionFieldsDeepMappedType3 +>SharedUnionFieldsDeepMappedType3 : Symbol(SharedUnionFieldsDeepMappedType3, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 36, 2)) +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 43, 28)) + + : {} + : T; +>T : Symbol(T, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 43, 28)) + +// doesn't distribute either +declare const actual3: SharedUnionFieldsDeep3< +>actual3 : Symbol(actual3, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 55, 13)) +>SharedUnionFieldsDeep3 : Symbol(SharedUnionFieldsDeep3, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 41, 12)) + + { tuple: [number] } | { tuple: [number, string] } +>tuple : Symbol(tuple, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 56, 3)) +>tuple : Symbol(tuple, Decl(mappedTypeHomomorphismWithPriorKeyofTChecks1.ts, 56, 25)) + +>; + diff --git a/tsc/testdata/baselines/reference/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.types b/tsc/testdata/baselines/reference/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.types new file mode 100644 index 0000000000000..dae46e92623d3 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.types @@ -0,0 +1,97 @@ +//// [tests/cases/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.ts] //// + +=== mappedTypeHomomorphismWithPriorKeyofTChecks1.ts === +type SharedUnionFieldsDeep1 = [T] extends [unknown[]] +>SharedUnionFieldsDeep1 : SharedUnionFieldsDeep1 + + ? // Keep only tuple positions guaranteed across the union. + 1 extends T["length"] + ? [T[0]] + : T + : keyof T extends infer CommonKeys + ? ([CommonKeys] extends [never] ? true : false) extends false +>true : true +>false : false +>false : false + + ? { + [Key in keyof T]: SharedUnionFieldsDeep1; + } + : {} + : T; + +// distributes +declare const actual1: SharedUnionFieldsDeep1< +>actual1 : { tuple: [number]; } | { tuple: [number, string]; } + + { tuple: [number] } | { tuple: [number, string] } +>tuple : [number] +>tuple : [number, string] + +>; + +type SharedUnionFieldsDeep2 = [T] extends [unknown[]] +>SharedUnionFieldsDeep2 : SharedUnionFieldsDeep2 + + ? // Keep only tuple positions guaranteed across the union. + 1 extends T["length"] + ? [T[0]] + : T + : keyof T extends infer CommonKeys + ? ([CommonKeys] extends [never] ? true : false) extends false +>true : true +>false : false +>false : false + + ? keyof T extends infer Keys extends PropertyKey + ? { + [Key in Keys]: SharedUnionFieldsDeep2; + } + : never + : {} + : T; + +// doesn't distribute +declare const actual2: SharedUnionFieldsDeep2< +>actual2 : { tuple: [number]; } + + { tuple: [number] } | { tuple: [number, string] } +>tuple : [number] +>tuple : [number, string] + +>; + +// `& unknown` used here just to avoid printing type alias at actual3 +type SharedUnionFieldsDeepMappedType3 = { +>SharedUnionFieldsDeepMappedType3 : { [Key in K]: SharedUnionFieldsDeep3; } + + [Key in K]: SharedUnionFieldsDeep3; +} & unknown; + +type SharedUnionFieldsDeep3 = [T] extends [unknown[]] +>SharedUnionFieldsDeep3 : SharedUnionFieldsDeep3 + + ? // Keep only tuple positions guaranteed across the union. + 1 extends T["length"] + ? [T[0]] + : T + : keyof T extends infer CommonKeys + ? ([CommonKeys] extends [never] ? true : false) extends false +>true : true +>false : false +>false : false + + ? SharedUnionFieldsDeepMappedType3 + : {} + : T; + +// doesn't distribute either +declare const actual3: SharedUnionFieldsDeep3< +>actual3 : { tuple: [number]; } + + { tuple: [number] } | { tuple: [number, string] } +>tuple : [number] +>tuple : [number, string] + +>; + diff --git a/tsc/testdata/baselines/reference/compiler/mappedTypeNotMistakenlyHomomorphic2.symbols b/tsc/testdata/baselines/reference/compiler/mappedTypeNotMistakenlyHomomorphic2.symbols new file mode 100644 index 0000000000000..f863319b058b7 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/mappedTypeNotMistakenlyHomomorphic2.symbols @@ -0,0 +1,39 @@ +//// [tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic2.ts] //// + +=== mappedTypeNotMistakenlyHomomorphic2.ts === +// https://github.com/microsoft/TypeScript/issues/63132 + +type KeyMap = keyof T extends PropertyKey ? { [K in keyof T]: K } : never; +>KeyMap : Symbol(KeyMap, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 0, 0)) +>T : Symbol(T, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 2, 12)) +>T : Symbol(T, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 2, 12)) +>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --)) +>K : Symbol(K, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 2, 50)) +>T : Symbol(T, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 2, 12)) +>K : Symbol(K, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 2, 50)) + +type X = KeyMap; +>X : Symbol(X, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 2, 77)) +>KeyMap : Symbol(KeyMap, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 0, 0)) + +type U = { a?: number; b: string } | { b: string; readonly c: boolean }; +>U : Symbol(U, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 3, 24)) +>a : Symbol(a, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 5, 10)) +>b : Symbol(b, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 5, 22)) +>b : Symbol(b, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 5, 38)) +>c : Symbol(c, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 5, 49)) + +type MapStringOnly = keyof T extends string ? { [K in keyof T]: [T[K]] } : never; +>MapStringOnly : Symbol(MapStringOnly, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 5, 72)) +>T : Symbol(T, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 6, 19)) +>T : Symbol(T, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 6, 19)) +>K : Symbol(K, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 6, 52)) +>T : Symbol(T, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 6, 19)) +>T : Symbol(T, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 6, 19)) +>K : Symbol(K, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 6, 52)) + +type Y = MapStringOnly; +>Y : Symbol(Y, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 6, 84)) +>MapStringOnly : Symbol(MapStringOnly, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 5, 72)) +>U : Symbol(U, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 3, 24)) + diff --git a/tsc/testdata/baselines/reference/compiler/mappedTypeNotMistakenlyHomomorphic2.types b/tsc/testdata/baselines/reference/compiler/mappedTypeNotMistakenlyHomomorphic2.types new file mode 100644 index 0000000000000..6cccb9bf1ca67 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/mappedTypeNotMistakenlyHomomorphic2.types @@ -0,0 +1,24 @@ +//// [tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic2.ts] //// + +=== mappedTypeNotMistakenlyHomomorphic2.ts === +// https://github.com/microsoft/TypeScript/issues/63132 + +type KeyMap = keyof T extends PropertyKey ? { [K in keyof T]: K } : never; +>KeyMap : KeyMap + +type X = KeyMap; +>X : number + +type U = { a?: number; b: string } | { b: string; readonly c: boolean }; +>U : U +>a : number | undefined +>b : string +>b : string +>c : boolean + +type MapStringOnly = keyof T extends string ? { [K in keyof T]: [T[K]] } : never; +>MapStringOnly : MapStringOnly + +type Y = MapStringOnly; +>Y : { a?: [number | undefined] | undefined; b: [string]; } | { b: [string]; readonly c: [boolean]; } + diff --git a/tsc/testdata/tests/cases/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.ts b/tsc/testdata/tests/cases/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.ts new file mode 100644 index 0000000000000..c279131823125 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.ts @@ -0,0 +1,58 @@ +type SharedUnionFieldsDeep1 = [T] extends [unknown[]] + ? // Keep only tuple positions guaranteed across the union. + 1 extends T["length"] + ? [T[0]] + : T + : keyof T extends infer CommonKeys + ? ([CommonKeys] extends [never] ? true : false) extends false + ? { + [Key in keyof T]: SharedUnionFieldsDeep1; + } + : {} + : T; + +// distributes +declare const actual1: SharedUnionFieldsDeep1< + { tuple: [number] } | { tuple: [number, string] } +>; + +type SharedUnionFieldsDeep2 = [T] extends [unknown[]] + ? // Keep only tuple positions guaranteed across the union. + 1 extends T["length"] + ? [T[0]] + : T + : keyof T extends infer CommonKeys + ? ([CommonKeys] extends [never] ? true : false) extends false + ? keyof T extends infer Keys extends PropertyKey + ? { + [Key in Keys]: SharedUnionFieldsDeep2; + } + : never + : {} + : T; + +// doesn't distribute +declare const actual2: SharedUnionFieldsDeep2< + { tuple: [number] } | { tuple: [number, string] } +>; + +// `& unknown` used here just to avoid printing type alias at actual3 +type SharedUnionFieldsDeepMappedType3 = { + [Key in K]: SharedUnionFieldsDeep3; +} & unknown; + +type SharedUnionFieldsDeep3 = [T] extends [unknown[]] + ? // Keep only tuple positions guaranteed across the union. + 1 extends T["length"] + ? [T[0]] + : T + : keyof T extends infer CommonKeys + ? ([CommonKeys] extends [never] ? true : false) extends false + ? SharedUnionFieldsDeepMappedType3 + : {} + : T; + +// doesn't distribute either +declare const actual3: SharedUnionFieldsDeep3< + { tuple: [number] } | { tuple: [number, string] } +>; diff --git a/tsc/testdata/tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic2.ts b/tsc/testdata/tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic2.ts new file mode 100644 index 0000000000000..2e4a917153061 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic2.ts @@ -0,0 +1,11 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/63132 + +type KeyMap = keyof T extends PropertyKey ? { [K in keyof T]: K } : never; +type X = KeyMap; + +type U = { a?: number; b: string } | { b: string; readonly c: boolean }; +type MapStringOnly = keyof T extends string ? { [K in keyof T]: [T[K]] } : never; +type Y = MapStringOnly;