diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6da3c9e760c3f..862fb23fa361e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11780,6 +11780,12 @@ namespace ts { return mapType(type as UnionType, getLowerBoundOfKeyType); } if (type.flags & TypeFlags.Intersection) { + // Similarly to getTypeFromIntersectionTypeNode, we preserve the special string & {}, number & {}, + // and bigint & {} intersections that are used to prevent subtype reduction in union types. + const types = (type as IntersectionType).types; + if (types.length === 2 && !!(types[0].flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)) && types[1] === emptyTypeLiteralType) { + return type; + } return getIntersectionType(sameMap((type as UnionType).types, getLowerBoundOfKeyType)); } return type; diff --git a/tests/baselines/reference/specialIntersectionsInMappedTypes.errors.txt b/tests/baselines/reference/specialIntersectionsInMappedTypes.errors.txt new file mode 100644 index 0000000000000..31c9404989468 --- /dev/null +++ b/tests/baselines/reference/specialIntersectionsInMappedTypes.errors.txt @@ -0,0 +1,21 @@ +tests/cases/compiler/specialIntersectionsInMappedTypes.ts(14,1): error TS2532: Object is possibly 'undefined'. + + +==== tests/cases/compiler/specialIntersectionsInMappedTypes.ts (1 errors) ==== + // Repro from #50683 + + type Alignment = (string & {}) | "left" | "center" | "right"; + type Alignments = Record; + + const a: Alignments = { + left: "align-left", + center: "align-center", + right: "align-right", + other: "align-other", + }; + + a.left.length; + a.other.length; // Error expected here + ~~~~~~~ +!!! error TS2532: Object is possibly 'undefined'. + \ No newline at end of file diff --git a/tests/baselines/reference/specialIntersectionsInMappedTypes.js b/tests/baselines/reference/specialIntersectionsInMappedTypes.js new file mode 100644 index 0000000000000..d06d8cb64b54b --- /dev/null +++ b/tests/baselines/reference/specialIntersectionsInMappedTypes.js @@ -0,0 +1,28 @@ +//// [specialIntersectionsInMappedTypes.ts] +// Repro from #50683 + +type Alignment = (string & {}) | "left" | "center" | "right"; +type Alignments = Record; + +const a: Alignments = { + left: "align-left", + center: "align-center", + right: "align-right", + other: "align-other", +}; + +a.left.length; +a.other.length; // Error expected here + + +//// [specialIntersectionsInMappedTypes.js] +"use strict"; +// Repro from #50683 +var a = { + left: "align-left", + center: "align-center", + right: "align-right", + other: "align-other" +}; +a.left.length; +a.other.length; // Error expected here diff --git a/tests/baselines/reference/specialIntersectionsInMappedTypes.symbols b/tests/baselines/reference/specialIntersectionsInMappedTypes.symbols new file mode 100644 index 0000000000000..d7ba7d4df4c05 --- /dev/null +++ b/tests/baselines/reference/specialIntersectionsInMappedTypes.symbols @@ -0,0 +1,41 @@ +=== tests/cases/compiler/specialIntersectionsInMappedTypes.ts === +// Repro from #50683 + +type Alignment = (string & {}) | "left" | "center" | "right"; +>Alignment : Symbol(Alignment, Decl(specialIntersectionsInMappedTypes.ts, 0, 0)) + +type Alignments = Record; +>Alignments : Symbol(Alignments, Decl(specialIntersectionsInMappedTypes.ts, 2, 61)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>Alignment : Symbol(Alignment, Decl(specialIntersectionsInMappedTypes.ts, 0, 0)) + +const a: Alignments = { +>a : Symbol(a, Decl(specialIntersectionsInMappedTypes.ts, 5, 5)) +>Alignments : Symbol(Alignments, Decl(specialIntersectionsInMappedTypes.ts, 2, 61)) + + left: "align-left", +>left : Symbol(left, Decl(specialIntersectionsInMappedTypes.ts, 5, 23)) + + center: "align-center", +>center : Symbol(center, Decl(specialIntersectionsInMappedTypes.ts, 6, 23)) + + right: "align-right", +>right : Symbol(right, Decl(specialIntersectionsInMappedTypes.ts, 7, 27)) + + other: "align-other", +>other : Symbol(other, Decl(specialIntersectionsInMappedTypes.ts, 8, 25)) + +}; + +a.left.length; +>a.left.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) +>a.left : Symbol(left) +>a : Symbol(a, Decl(specialIntersectionsInMappedTypes.ts, 5, 5)) +>left : Symbol(left) +>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) + +a.other.length; // Error expected here +>a.other.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(specialIntersectionsInMappedTypes.ts, 5, 5)) +>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) + diff --git a/tests/baselines/reference/specialIntersectionsInMappedTypes.types b/tests/baselines/reference/specialIntersectionsInMappedTypes.types new file mode 100644 index 0000000000000..7fda315f8a3ca --- /dev/null +++ b/tests/baselines/reference/specialIntersectionsInMappedTypes.types @@ -0,0 +1,45 @@ +=== tests/cases/compiler/specialIntersectionsInMappedTypes.ts === +// Repro from #50683 + +type Alignment = (string & {}) | "left" | "center" | "right"; +>Alignment : (string & {}) | "left" | "center" | "right" + +type Alignments = Record; +>Alignments : { [x: string & {}]: string; left: string; center: string; right: string; } + +const a: Alignments = { +>a : Alignments +>{ left: "align-left", center: "align-center", right: "align-right", other: "align-other",} : { left: string; center: string; right: string; other: string; } + + left: "align-left", +>left : string +>"align-left" : "align-left" + + center: "align-center", +>center : string +>"align-center" : "align-center" + + right: "align-right", +>right : string +>"align-right" : "align-right" + + other: "align-other", +>other : string +>"align-other" : "align-other" + +}; + +a.left.length; +>a.left.length : number +>a.left : string +>a : Alignments +>left : string +>length : number + +a.other.length; // Error expected here +>a.other.length : number +>a.other : string | undefined +>a : Alignments +>other : string | undefined +>length : number + diff --git a/tests/cases/compiler/specialIntersectionsInMappedTypes.ts b/tests/cases/compiler/specialIntersectionsInMappedTypes.ts new file mode 100644 index 0000000000000..9341a0f3ae98a --- /dev/null +++ b/tests/cases/compiler/specialIntersectionsInMappedTypes.ts @@ -0,0 +1,17 @@ +// @strict: true +// @noUncheckedIndexedAccess: true + +// Repro from #50683 + +type Alignment = (string & {}) | "left" | "center" | "right"; +type Alignments = Record; + +const a: Alignments = { + left: "align-left", + center: "align-center", + right: "align-right", + other: "align-other", +}; + +a.left.length; +a.other.length; // Error expected here