Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion tsc/internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//// [tests/cases/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.ts] ////

//// [mappedTypeHomomorphismWithPriorKeyofTChecks1.ts]
type SharedUnionFieldsDeep1<T> = [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[Key]>;
}
: {}
: T;

// distributes
declare const actual1: SharedUnionFieldsDeep1<
{ tuple: [number] } | { tuple: [number, string] }
>;

type SharedUnionFieldsDeep2<T> = [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<T[Key & keyof T]>;
}
: 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<T, K extends keyof T = keyof T> = {
[Key in K]: SharedUnionFieldsDeep3<T[Key]>;
} & unknown;

type SharedUnionFieldsDeep3<T> = [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>
: {}
: T;

// doesn't distribute either
declare const actual3: SharedUnionFieldsDeep3<
{ tuple: [number] } | { tuple: [number, string] }
>;


//// [mappedTypeHomomorphismWithPriorKeyofTChecks1.js]
"use strict";
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
//// [tests/cases/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.ts] ////

=== mappedTypeHomomorphismWithPriorKeyofTChecks1.ts ===
type SharedUnionFieldsDeep1<T> = [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<T[Key]>;
>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> = [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<T[Key & keyof T]>;
>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<T, K extends keyof T = keyof T> = {
>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<T[Key]>;
>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> = [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<T>
>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))

>;

Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//// [tests/cases/compiler/mappedTypeHomomorphismWithPriorKeyofTChecks1.ts] ////

=== mappedTypeHomomorphismWithPriorKeyofTChecks1.ts ===
type SharedUnionFieldsDeep1<T> = [T] extends [unknown[]]
>SharedUnionFieldsDeep1 : SharedUnionFieldsDeep1<T>

? // 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[Key]>;
}
: {}
: 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> = [T] extends [unknown[]]
>SharedUnionFieldsDeep2 : SharedUnionFieldsDeep2<T>

? // 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<T[Key & keyof T]>;
}
: 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<T, K extends keyof T = keyof T> = {
>SharedUnionFieldsDeepMappedType3 : { [Key in K]: SharedUnionFieldsDeep3<T[Key]>; }

[Key in K]: SharedUnionFieldsDeep3<T[Key]>;
} & unknown;

type SharedUnionFieldsDeep3<T> = [T] extends [unknown[]]
>SharedUnionFieldsDeep3 : SharedUnionFieldsDeep3<T>

? // 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>
: {}
: T;

// doesn't distribute either
declare const actual3: SharedUnionFieldsDeep3<
>actual3 : { tuple: [number]; }

{ tuple: [number] } | { tuple: [number, string] }
>tuple : [number]
>tuple : [number, string]

>;

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//// [tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic2.ts] ////

=== mappedTypeNotMistakenlyHomomorphic2.ts ===
// https://github.com/microsoft/TypeScript/issues/63132

type KeyMap<T> = 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<number>;
>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<T> = 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<U>;
>Y : Symbol(Y, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 6, 84))
>MapStringOnly : Symbol(MapStringOnly, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 5, 72))
>U : Symbol(U, Decl(mappedTypeNotMistakenlyHomomorphic2.ts, 3, 24))

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//// [tests/cases/compiler/mappedTypeNotMistakenlyHomomorphic2.ts] ////

=== mappedTypeNotMistakenlyHomomorphic2.ts ===
// https://github.com/microsoft/TypeScript/issues/63132

type KeyMap<T> = keyof T extends PropertyKey ? { [K in keyof T]: K } : never;
>KeyMap : KeyMap<T>

type X = KeyMap<number>;
>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<T> = keyof T extends string ? { [K in keyof T]: [T[K]] } : never;
>MapStringOnly : MapStringOnly<T>

type Y = MapStringOnly<U>;
>Y : { a?: [number | undefined] | undefined; b: [string]; } | { b: [string]; readonly c: [boolean]; }

Loading