Skip to content
Permalink
Browse files
Preserve special intersections in mapped types (#50704)
* Preserve special intersections in mapped types

* Add regression test
  • Loading branch information
ahejlsberg committed Sep 9, 2022
1 parent 1a1c271 commit a70bb9d3ff102e044e83d2db43e585105e9a1761
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 0 deletions.
@@ -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;
@@ -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<Alignment, string>;

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'.

@@ -0,0 +1,28 @@
//// [specialIntersectionsInMappedTypes.ts]
// Repro from #50683

type Alignment = (string & {}) | "left" | "center" | "right";
type Alignments = Record<Alignment, string>;

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
@@ -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<Alignment, string>;
>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, --, --))

@@ -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<Alignment, string>;
>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

@@ -0,0 +1,17 @@
// @strict: true
// @noUncheckedIndexedAccess: true

// Repro from #50683

type Alignment = (string & {}) | "left" | "center" | "right";
type Alignments = Record<Alignment, string>;

const a: Alignments = {
left: "align-left",
center: "align-center",
right: "align-right",
other: "align-other",
};

a.left.length;
a.other.length; // Error expected here

0 comments on commit a70bb9d

Please sign in to comment.