From 0c18a81d7a43cadf487276860089742c45c5a3ce Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 17 Feb 2023 20:53:23 -0800 Subject: [PATCH 01/12] Add test --- ...stedTemplateLiteralIntersection.errors.txt | 29 +++++++++++++++++++ ...deeplyNestedTemplateLiteralIntersection.ts | 27 +++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 tests/baselines/reference/deeplyNestedTemplateLiteralIntersection.errors.txt create mode 100644 tests/cases/compiler/deeplyNestedTemplateLiteralIntersection.ts diff --git a/tests/baselines/reference/deeplyNestedTemplateLiteralIntersection.errors.txt b/tests/baselines/reference/deeplyNestedTemplateLiteralIntersection.errors.txt new file mode 100644 index 0000000000000..b05c63b4b4be1 --- /dev/null +++ b/tests/baselines/reference/deeplyNestedTemplateLiteralIntersection.errors.txt @@ -0,0 +1,29 @@ +tests/cases/compiler/deeplyNestedTemplateLiteralIntersection.ts(20,11): error TS2590: Expression produces a union type that is too complex to represent. + + +==== tests/cases/compiler/deeplyNestedTemplateLiteralIntersection.ts (1 errors) ==== + type R = `${number}a` & { + _thing: true; + }; + + type _S = "1" | "2" | "3" | "4" | "5" | "6"; + + type S = `${_S}${_S}${_S}`; + + + type T = R | S; + type X = `${T} ${T}`; + + export type Props = Partial<{ + x: X; + }>; + + const a1: Props = {}; + const a2: Props = {}; + + const b = { ...a1, ...a2 }; + ~~~~~~~~~~~~~~~~ +!!! error TS2590: Expression produces a union type that is too complex to represent. + + export { b }; + \ No newline at end of file diff --git a/tests/cases/compiler/deeplyNestedTemplateLiteralIntersection.ts b/tests/cases/compiler/deeplyNestedTemplateLiteralIntersection.ts new file mode 100644 index 0000000000000..9af003f26c303 --- /dev/null +++ b/tests/cases/compiler/deeplyNestedTemplateLiteralIntersection.ts @@ -0,0 +1,27 @@ +// @strict: true +// @noEmit: true +// @noTypesAndSymbols: true + + +type R = `${number}a` & { + _thing: true; +}; + +type _S = "1" | "2" | "3" | "4" | "5" | "6"; + +type S = `${_S}${_S}${_S}`; + + +type T = R | S; +type X = `${T} ${T}`; + +export type Props = Partial<{ + x: X; +}>; + +const a1: Props = {}; +const a2: Props = {}; + +const b = { ...a1, ...a2 }; + +export { b }; From 1dd84d2afea54d76dce64274ff25a5c2438ab24f Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 17 Feb 2023 21:27:56 -0800 Subject: [PATCH 02/12] Add new test --- .../templateLiteralIntersection2.errors.txt | 38 +++++++++++ .../templateLiteralIntersection2.symbols | 56 ++++++++++++++++ .../templateLiteralIntersection2.types | 64 +++++++++++++++++++ .../compiler/templateLiteralIntersection2.ts | 27 ++++++++ 4 files changed, 185 insertions(+) create mode 100644 tests/baselines/reference/templateLiteralIntersection2.errors.txt create mode 100644 tests/baselines/reference/templateLiteralIntersection2.symbols create mode 100644 tests/baselines/reference/templateLiteralIntersection2.types create mode 100644 tests/cases/compiler/templateLiteralIntersection2.ts diff --git a/tests/baselines/reference/templateLiteralIntersection2.errors.txt b/tests/baselines/reference/templateLiteralIntersection2.errors.txt new file mode 100644 index 0000000000000..9bf6d9805b0eb --- /dev/null +++ b/tests/baselines/reference/templateLiteralIntersection2.errors.txt @@ -0,0 +1,38 @@ +tests/cases/compiler/templateLiteralIntersection2.ts(20,10): error TS2345: Argument of type '""' is not assignable to parameter of type '`a${string}` & `${string}a`'. + Type '""' is not assignable to type '`a${string}`'. +tests/cases/compiler/templateLiteralIntersection2.ts(22,10): error TS2345: Argument of type '"ab"' is not assignable to parameter of type '`a${string}` & `${string}a`'. + Type '"ab"' is not assignable to type '`${string}a`'. + + +==== tests/cases/compiler/templateLiteralIntersection2.ts (2 errors) ==== + type Path = string & { _pathBrand: any }; + + type JoinedPath = `${Path}/${Path}`; + + declare function joinedPath(p: JoinedPath): void; + + joinedPath("foo/bar"); + + declare const somePath: Path; + + joinedPath(`${somePath}/${somePath}`); + + + type StartsWithA = `a${string}`; + type EndsWithA = `${string}a`; + + + declare function withinAs(p: StartsWithA & EndsWithA): void; + + withinAs(""); + ~~ +!!! error TS2345: Argument of type '""' is not assignable to parameter of type '`a${string}` & `${string}a`'. +!!! error TS2345: Type '""' is not assignable to type '`a${string}`'. + withinAs("a"); + withinAs("ab"); + ~~~~ +!!! error TS2345: Argument of type '"ab"' is not assignable to parameter of type '`a${string}` & `${string}a`'. +!!! error TS2345: Type '"ab"' is not assignable to type '`${string}a`'. + withinAs("aba"); + withinAs("abavvvva"); + \ No newline at end of file diff --git a/tests/baselines/reference/templateLiteralIntersection2.symbols b/tests/baselines/reference/templateLiteralIntersection2.symbols new file mode 100644 index 0000000000000..5e97ed68f8899 --- /dev/null +++ b/tests/baselines/reference/templateLiteralIntersection2.symbols @@ -0,0 +1,56 @@ +=== tests/cases/compiler/templateLiteralIntersection2.ts === +type Path = string & { _pathBrand: any }; +>Path : Symbol(Path, Decl(templateLiteralIntersection2.ts, 0, 0)) +>_pathBrand : Symbol(_pathBrand, Decl(templateLiteralIntersection2.ts, 0, 22)) + +type JoinedPath = `${Path}/${Path}`; +>JoinedPath : Symbol(JoinedPath, Decl(templateLiteralIntersection2.ts, 0, 41)) +>Path : Symbol(Path, Decl(templateLiteralIntersection2.ts, 0, 0)) +>Path : Symbol(Path, Decl(templateLiteralIntersection2.ts, 0, 0)) + +declare function joinedPath(p: JoinedPath): void; +>joinedPath : Symbol(joinedPath, Decl(templateLiteralIntersection2.ts, 2, 36)) +>p : Symbol(p, Decl(templateLiteralIntersection2.ts, 4, 28)) +>JoinedPath : Symbol(JoinedPath, Decl(templateLiteralIntersection2.ts, 0, 41)) + +joinedPath("foo/bar"); +>joinedPath : Symbol(joinedPath, Decl(templateLiteralIntersection2.ts, 2, 36)) + +declare const somePath: Path; +>somePath : Symbol(somePath, Decl(templateLiteralIntersection2.ts, 8, 13)) +>Path : Symbol(Path, Decl(templateLiteralIntersection2.ts, 0, 0)) + +joinedPath(`${somePath}/${somePath}`); +>joinedPath : Symbol(joinedPath, Decl(templateLiteralIntersection2.ts, 2, 36)) +>somePath : Symbol(somePath, Decl(templateLiteralIntersection2.ts, 8, 13)) +>somePath : Symbol(somePath, Decl(templateLiteralIntersection2.ts, 8, 13)) + + +type StartsWithA = `a${string}`; +>StartsWithA : Symbol(StartsWithA, Decl(templateLiteralIntersection2.ts, 10, 38)) + +type EndsWithA = `${string}a`; +>EndsWithA : Symbol(EndsWithA, Decl(templateLiteralIntersection2.ts, 13, 32)) + + +declare function withinAs(p: StartsWithA & EndsWithA): void; +>withinAs : Symbol(withinAs, Decl(templateLiteralIntersection2.ts, 14, 30)) +>p : Symbol(p, Decl(templateLiteralIntersection2.ts, 17, 26)) +>StartsWithA : Symbol(StartsWithA, Decl(templateLiteralIntersection2.ts, 10, 38)) +>EndsWithA : Symbol(EndsWithA, Decl(templateLiteralIntersection2.ts, 13, 32)) + +withinAs(""); +>withinAs : Symbol(withinAs, Decl(templateLiteralIntersection2.ts, 14, 30)) + +withinAs("a"); +>withinAs : Symbol(withinAs, Decl(templateLiteralIntersection2.ts, 14, 30)) + +withinAs("ab"); +>withinAs : Symbol(withinAs, Decl(templateLiteralIntersection2.ts, 14, 30)) + +withinAs("aba"); +>withinAs : Symbol(withinAs, Decl(templateLiteralIntersection2.ts, 14, 30)) + +withinAs("abavvvva"); +>withinAs : Symbol(withinAs, Decl(templateLiteralIntersection2.ts, 14, 30)) + diff --git a/tests/baselines/reference/templateLiteralIntersection2.types b/tests/baselines/reference/templateLiteralIntersection2.types new file mode 100644 index 0000000000000..577f6edbf291b --- /dev/null +++ b/tests/baselines/reference/templateLiteralIntersection2.types @@ -0,0 +1,64 @@ +=== tests/cases/compiler/templateLiteralIntersection2.ts === +type Path = string & { _pathBrand: any }; +>Path : string & { _pathBrand: any; } +>_pathBrand : any + +type JoinedPath = `${Path}/${Path}`; +>JoinedPath : `${string}/${string}` + +declare function joinedPath(p: JoinedPath): void; +>joinedPath : (p: JoinedPath) => void +>p : `${string}/${string}` + +joinedPath("foo/bar"); +>joinedPath("foo/bar") : void +>joinedPath : (p: `${string}/${string}`) => void +>"foo/bar" : "foo/bar" + +declare const somePath: Path; +>somePath : Path + +joinedPath(`${somePath}/${somePath}`); +>joinedPath(`${somePath}/${somePath}`) : void +>joinedPath : (p: `${string}/${string}`) => void +>`${somePath}/${somePath}` : `${string}/${string}` +>somePath : Path +>somePath : Path + + +type StartsWithA = `a${string}`; +>StartsWithA : `a${string}` + +type EndsWithA = `${string}a`; +>EndsWithA : `${string}a` + + +declare function withinAs(p: StartsWithA & EndsWithA): void; +>withinAs : (p: StartsWithA & EndsWithA) => void +>p : `a${string}` & `${string}a` + +withinAs(""); +>withinAs("") : void +>withinAs : (p: `a${string}` & `${string}a`) => void +>"" : "" + +withinAs("a"); +>withinAs("a") : void +>withinAs : (p: `a${string}` & `${string}a`) => void +>"a" : "a" + +withinAs("ab"); +>withinAs("ab") : void +>withinAs : (p: `a${string}` & `${string}a`) => void +>"ab" : "ab" + +withinAs("aba"); +>withinAs("aba") : void +>withinAs : (p: `a${string}` & `${string}a`) => void +>"aba" : "aba" + +withinAs("abavvvva"); +>withinAs("abavvvva") : void +>withinAs : (p: `a${string}` & `${string}a`) => void +>"abavvvva" : "abavvvva" + diff --git a/tests/cases/compiler/templateLiteralIntersection2.ts b/tests/cases/compiler/templateLiteralIntersection2.ts new file mode 100644 index 0000000000000..7b9ca947bd2bf --- /dev/null +++ b/tests/cases/compiler/templateLiteralIntersection2.ts @@ -0,0 +1,27 @@ +// @strict: true +// @noEmit: true + +type Path = string & { _pathBrand: any }; + +type JoinedPath = `${Path}/${Path}`; + +declare function joinedPath(p: JoinedPath): void; + +joinedPath("foo/bar"); + +declare const somePath: Path; + +joinedPath(`${somePath}/${somePath}`); + + +type StartsWithA = `a${string}`; +type EndsWithA = `${string}a`; + + +declare function withinAs(p: StartsWithA & EndsWithA): void; + +withinAs(""); +withinAs("a"); +withinAs("ab"); +withinAs("aba"); +withinAs("abavvvva"); From 8b1f1e5eba24d298a0955e7f0b48d500184deea8 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 17 Feb 2023 21:03:32 -0800 Subject: [PATCH 03/12] Revert PR 48044 --- src/compiler/checker.ts | 19 ++++-------- ...stedTemplateLiteralIntersection.errors.txt | 29 ------------------- .../templateLiteralIntersection.types | 8 ++--- .../templateLiteralIntersection2.types | 10 +++---- 4 files changed, 15 insertions(+), 51 deletions(-) delete mode 100644 tests/baselines/reference/deeplyNestedTemplateLiteralIntersection.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 91ff98be94646..2629fc53e71d3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16971,6 +16971,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getTemplateLiteralType(texts: readonly string[], types: readonly Type[]): Type { + // TODO(jakebailey): filter out intersections here? const unionIndex = findIndex(types, t => !!(t.flags & (TypeFlags.Never | TypeFlags.Union))); if (unionIndex >= 0) { return checkCrossProductUnion(types) ? @@ -17006,32 +17007,24 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } return type; - function addSpans(texts: readonly string[] | string, types: readonly Type[]): boolean { - const isTextsArray = isArray(texts); + function addSpans(texts: readonly string[], types: readonly Type[]): boolean { for (let i = 0; i < types.length; i++) { const t = types[i]; - const addText = isTextsArray ? texts[i + 1] : texts; if (t.flags & (TypeFlags.Literal | TypeFlags.Null | TypeFlags.Undefined)) { text += getTemplateStringForType(t) || ""; - text += addText; - if (!isTextsArray) return true; + text += texts[i + 1]; } else if (t.flags & TypeFlags.TemplateLiteral) { text += (t as TemplateLiteralType).texts[0]; if (!addSpans((t as TemplateLiteralType).texts, (t as TemplateLiteralType).types)) return false; - text += addText; - if (!isTextsArray) return true; + text += texts[i + 1]; } else if (isGenericIndexType(t) || isPatternLiteralPlaceholderType(t)) { newTypes.push(t); newTexts.push(text); - text = addText; + text = texts[i + 1]; } - else if (t.flags & TypeFlags.Intersection) { - const added = addSpans(texts[i + 1], (t as IntersectionType).types); - if (!added) return false; - } - else if (isTextsArray) { + else { return false; } } diff --git a/tests/baselines/reference/deeplyNestedTemplateLiteralIntersection.errors.txt b/tests/baselines/reference/deeplyNestedTemplateLiteralIntersection.errors.txt deleted file mode 100644 index b05c63b4b4be1..0000000000000 --- a/tests/baselines/reference/deeplyNestedTemplateLiteralIntersection.errors.txt +++ /dev/null @@ -1,29 +0,0 @@ -tests/cases/compiler/deeplyNestedTemplateLiteralIntersection.ts(20,11): error TS2590: Expression produces a union type that is too complex to represent. - - -==== tests/cases/compiler/deeplyNestedTemplateLiteralIntersection.ts (1 errors) ==== - type R = `${number}a` & { - _thing: true; - }; - - type _S = "1" | "2" | "3" | "4" | "5" | "6"; - - type S = `${_S}${_S}${_S}`; - - - type T = R | S; - type X = `${T} ${T}`; - - export type Props = Partial<{ - x: X; - }>; - - const a1: Props = {}; - const a2: Props = {}; - - const b = { ...a1, ...a2 }; - ~~~~~~~~~~~~~~~~ -!!! error TS2590: Expression produces a union type that is too complex to represent. - - export { b }; - \ No newline at end of file diff --git a/tests/baselines/reference/templateLiteralIntersection.types b/tests/baselines/reference/templateLiteralIntersection.types index 0c716d40a731d..94ccb01964c94 100644 --- a/tests/baselines/reference/templateLiteralIntersection.types +++ b/tests/baselines/reference/templateLiteralIntersection.types @@ -16,7 +16,7 @@ type OriginA1 = `${A}` >OriginA1 : "a" type OriginA2 = `${MixA}` ->OriginA2 : "a" +>OriginA2 : string type B = `${typeof a}` >B : "a" @@ -30,14 +30,14 @@ type OriginB1 = `${B}` >OriginB1 : "a" type OriginB2 = `${MixB}` ->OriginB2 : "a" +>OriginB2 : string type MixC = { foo: string } & A >MixC : { foo: string; } & "a" >foo : string type OriginC = `${MixC}` ->OriginC : "a" +>OriginC : string type MixD = >MixD : `${T & { foo: string; }}` @@ -46,7 +46,7 @@ type MixD = >foo : string type OriginD = `${MixD & { foo: string }}`; ->OriginD : "a" +>OriginD : string >foo : string >foo : string diff --git a/tests/baselines/reference/templateLiteralIntersection2.types b/tests/baselines/reference/templateLiteralIntersection2.types index 577f6edbf291b..152eae769b3b8 100644 --- a/tests/baselines/reference/templateLiteralIntersection2.types +++ b/tests/baselines/reference/templateLiteralIntersection2.types @@ -4,15 +4,15 @@ type Path = string & { _pathBrand: any }; >_pathBrand : any type JoinedPath = `${Path}/${Path}`; ->JoinedPath : `${string}/${string}` +>JoinedPath : string declare function joinedPath(p: JoinedPath): void; >joinedPath : (p: JoinedPath) => void ->p : `${string}/${string}` +>p : string joinedPath("foo/bar"); >joinedPath("foo/bar") : void ->joinedPath : (p: `${string}/${string}`) => void +>joinedPath : (p: string) => void >"foo/bar" : "foo/bar" declare const somePath: Path; @@ -20,8 +20,8 @@ declare const somePath: Path; joinedPath(`${somePath}/${somePath}`); >joinedPath(`${somePath}/${somePath}`) : void ->joinedPath : (p: `${string}/${string}`) => void ->`${somePath}/${somePath}` : `${string}/${string}` +>joinedPath : (p: string) => void +>`${somePath}/${somePath}` : string >somePath : Path >somePath : Path From bb599af35210b6eb5273fe9e1c9b471bac4c2ece Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 17 Feb 2023 21:48:16 -0800 Subject: [PATCH 04/12] Implement new fix for intersections --- src/compiler/checker.ts | 2 +- ...stedTemplateLiteralIntersection.errors.txt | 29 +++++++++++++++++++ .../templateLiteralIntersection.types | 8 ++--- .../templateLiteralIntersection2.errors.txt | 5 +++- .../templateLiteralIntersection2.types | 10 +++---- 5 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 tests/baselines/reference/deeplyNestedTemplateLiteralIntersection.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2629fc53e71d3..6cdd0a270198d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17019,7 +17019,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!addSpans((t as TemplateLiteralType).texts, (t as TemplateLiteralType).types)) return false; text += texts[i + 1]; } - else if (isGenericIndexType(t) || isPatternLiteralPlaceholderType(t)) { + else if (isGenericIndexType(t) || isPatternLiteralPlaceholderType(t) || t.flags & TypeFlags.Intersection) { newTypes.push(t); newTexts.push(text); text = texts[i + 1]; diff --git a/tests/baselines/reference/deeplyNestedTemplateLiteralIntersection.errors.txt b/tests/baselines/reference/deeplyNestedTemplateLiteralIntersection.errors.txt new file mode 100644 index 0000000000000..b05c63b4b4be1 --- /dev/null +++ b/tests/baselines/reference/deeplyNestedTemplateLiteralIntersection.errors.txt @@ -0,0 +1,29 @@ +tests/cases/compiler/deeplyNestedTemplateLiteralIntersection.ts(20,11): error TS2590: Expression produces a union type that is too complex to represent. + + +==== tests/cases/compiler/deeplyNestedTemplateLiteralIntersection.ts (1 errors) ==== + type R = `${number}a` & { + _thing: true; + }; + + type _S = "1" | "2" | "3" | "4" | "5" | "6"; + + type S = `${_S}${_S}${_S}`; + + + type T = R | S; + type X = `${T} ${T}`; + + export type Props = Partial<{ + x: X; + }>; + + const a1: Props = {}; + const a2: Props = {}; + + const b = { ...a1, ...a2 }; + ~~~~~~~~~~~~~~~~ +!!! error TS2590: Expression produces a union type that is too complex to represent. + + export { b }; + \ No newline at end of file diff --git a/tests/baselines/reference/templateLiteralIntersection.types b/tests/baselines/reference/templateLiteralIntersection.types index 94ccb01964c94..eb20f0977a8b9 100644 --- a/tests/baselines/reference/templateLiteralIntersection.types +++ b/tests/baselines/reference/templateLiteralIntersection.types @@ -16,7 +16,7 @@ type OriginA1 = `${A}` >OriginA1 : "a" type OriginA2 = `${MixA}` ->OriginA2 : string +>OriginA2 : `${MixA}` type B = `${typeof a}` >B : "a" @@ -30,14 +30,14 @@ type OriginB1 = `${B}` >OriginB1 : "a" type OriginB2 = `${MixB}` ->OriginB2 : string +>OriginB2 : `${MixB}` type MixC = { foo: string } & A >MixC : { foo: string; } & "a" >foo : string type OriginC = `${MixC}` ->OriginC : string +>OriginC : `${MixC}` type MixD = >MixD : `${T & { foo: string; }}` @@ -46,7 +46,7 @@ type MixD = >foo : string type OriginD = `${MixD & { foo: string }}`; ->OriginD : string +>OriginD : `${`${"a" & { foo: string; } & { foo: string; }}` & { foo: string; }}` >foo : string >foo : string diff --git a/tests/baselines/reference/templateLiteralIntersection2.errors.txt b/tests/baselines/reference/templateLiteralIntersection2.errors.txt index 9bf6d9805b0eb..fce5339a694e0 100644 --- a/tests/baselines/reference/templateLiteralIntersection2.errors.txt +++ b/tests/baselines/reference/templateLiteralIntersection2.errors.txt @@ -1,10 +1,11 @@ +tests/cases/compiler/templateLiteralIntersection2.ts(7,12): error TS2345: Argument of type '"foo/bar"' is not assignable to parameter of type '`${Path}/${Path}`'. tests/cases/compiler/templateLiteralIntersection2.ts(20,10): error TS2345: Argument of type '""' is not assignable to parameter of type '`a${string}` & `${string}a`'. Type '""' is not assignable to type '`a${string}`'. tests/cases/compiler/templateLiteralIntersection2.ts(22,10): error TS2345: Argument of type '"ab"' is not assignable to parameter of type '`a${string}` & `${string}a`'. Type '"ab"' is not assignable to type '`${string}a`'. -==== tests/cases/compiler/templateLiteralIntersection2.ts (2 errors) ==== +==== tests/cases/compiler/templateLiteralIntersection2.ts (3 errors) ==== type Path = string & { _pathBrand: any }; type JoinedPath = `${Path}/${Path}`; @@ -12,6 +13,8 @@ tests/cases/compiler/templateLiteralIntersection2.ts(22,10): error TS2345: Argum declare function joinedPath(p: JoinedPath): void; joinedPath("foo/bar"); + ~~~~~~~~~ +!!! error TS2345: Argument of type '"foo/bar"' is not assignable to parameter of type '`${Path}/${Path}`'. declare const somePath: Path; diff --git a/tests/baselines/reference/templateLiteralIntersection2.types b/tests/baselines/reference/templateLiteralIntersection2.types index 152eae769b3b8..a561c4e453632 100644 --- a/tests/baselines/reference/templateLiteralIntersection2.types +++ b/tests/baselines/reference/templateLiteralIntersection2.types @@ -4,15 +4,15 @@ type Path = string & { _pathBrand: any }; >_pathBrand : any type JoinedPath = `${Path}/${Path}`; ->JoinedPath : string +>JoinedPath : `${Path}/${Path}` declare function joinedPath(p: JoinedPath): void; >joinedPath : (p: JoinedPath) => void ->p : string +>p : `${Path}/${Path}` joinedPath("foo/bar"); >joinedPath("foo/bar") : void ->joinedPath : (p: string) => void +>joinedPath : (p: `${Path}/${Path}`) => void >"foo/bar" : "foo/bar" declare const somePath: Path; @@ -20,8 +20,8 @@ declare const somePath: Path; joinedPath(`${somePath}/${somePath}`); >joinedPath(`${somePath}/${somePath}`) : void ->joinedPath : (p: string) => void ->`${somePath}/${somePath}` : string +>joinedPath : (p: `${Path}/${Path}`) => void +>`${somePath}/${somePath}` : `${Path}/${Path}` >somePath : Path >somePath : Path From 1f07f63d0ed2fd8f0bdd37e99c0a6fc76d8d39c4 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 17 Feb 2023 21:51:25 -0800 Subject: [PATCH 05/12] Remove TODO --- src/compiler/checker.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6cdd0a270198d..40eb6533ae894 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16971,7 +16971,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getTemplateLiteralType(texts: readonly string[], types: readonly Type[]): Type { - // TODO(jakebailey): filter out intersections here? const unionIndex = findIndex(types, t => !!(t.flags & (TypeFlags.Never | TypeFlags.Union))); if (unionIndex >= 0) { return checkCrossProductUnion(types) ? From 8528a63d7c317c4e558f7f8ff31b6644b54f85f6 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sat, 18 Feb 2023 10:08:43 -0800 Subject: [PATCH 06/12] Add new failing test from top100 --- .../templateLiteralIntersection3.errors.txt | 15 ++++++++++ .../templateLiteralIntersection3.symbols | 21 ++++++++++++++ .../templateLiteralIntersection3.types | 28 +++++++++++++++++++ .../compiler/templateLiteralIntersection3.ts | 11 ++++++++ 4 files changed, 75 insertions(+) create mode 100644 tests/baselines/reference/templateLiteralIntersection3.errors.txt create mode 100644 tests/baselines/reference/templateLiteralIntersection3.symbols create mode 100644 tests/baselines/reference/templateLiteralIntersection3.types create mode 100644 tests/cases/compiler/templateLiteralIntersection3.ts diff --git a/tests/baselines/reference/templateLiteralIntersection3.errors.txt b/tests/baselines/reference/templateLiteralIntersection3.errors.txt new file mode 100644 index 0000000000000..d13f87246fbf3 --- /dev/null +++ b/tests/baselines/reference/templateLiteralIntersection3.errors.txt @@ -0,0 +1,15 @@ +tests/cases/compiler/templateLiteralIntersection3.ts(8,1): error TS2322: Type 'boolean' is not assignable to type '({ prop: number; } & { [k: string]: boolean; })[`foo/${Path}`]'. + + +==== tests/cases/compiler/templateLiteralIntersection3.ts (1 errors) ==== + type Path = string & { _pathBrand: any }; + declare const path: Path; + + declare const options1: { prop: number; } & { [k: string]: boolean; }; + + options1[`foo`] = false; + + options1[`foo/${path}`] = false; + ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type 'boolean' is not assignable to type '({ prop: number; } & { [k: string]: boolean; })[`foo/${Path}`]'. + \ No newline at end of file diff --git a/tests/baselines/reference/templateLiteralIntersection3.symbols b/tests/baselines/reference/templateLiteralIntersection3.symbols new file mode 100644 index 0000000000000..05d7fec8333b5 --- /dev/null +++ b/tests/baselines/reference/templateLiteralIntersection3.symbols @@ -0,0 +1,21 @@ +=== tests/cases/compiler/templateLiteralIntersection3.ts === +type Path = string & { _pathBrand: any }; +>Path : Symbol(Path, Decl(templateLiteralIntersection3.ts, 0, 0)) +>_pathBrand : Symbol(_pathBrand, Decl(templateLiteralIntersection3.ts, 0, 22)) + +declare const path: Path; +>path : Symbol(path, Decl(templateLiteralIntersection3.ts, 1, 13)) +>Path : Symbol(Path, Decl(templateLiteralIntersection3.ts, 0, 0)) + +declare const options1: { prop: number; } & { [k: string]: boolean; }; +>options1 : Symbol(options1, Decl(templateLiteralIntersection3.ts, 3, 13)) +>prop : Symbol(prop, Decl(templateLiteralIntersection3.ts, 3, 25)) +>k : Symbol(k, Decl(templateLiteralIntersection3.ts, 3, 47)) + +options1[`foo`] = false; +>options1 : Symbol(options1, Decl(templateLiteralIntersection3.ts, 3, 13)) + +options1[`foo/${path}`] = false; +>options1 : Symbol(options1, Decl(templateLiteralIntersection3.ts, 3, 13)) +>path : Symbol(path, Decl(templateLiteralIntersection3.ts, 1, 13)) + diff --git a/tests/baselines/reference/templateLiteralIntersection3.types b/tests/baselines/reference/templateLiteralIntersection3.types new file mode 100644 index 0000000000000..14c869b04d20e --- /dev/null +++ b/tests/baselines/reference/templateLiteralIntersection3.types @@ -0,0 +1,28 @@ +=== tests/cases/compiler/templateLiteralIntersection3.ts === +type Path = string & { _pathBrand: any }; +>Path : string & { _pathBrand: any; } +>_pathBrand : any + +declare const path: Path; +>path : Path + +declare const options1: { prop: number; } & { [k: string]: boolean; }; +>options1 : { prop: number; } & { [k: string]: boolean; } +>prop : number +>k : string + +options1[`foo`] = false; +>options1[`foo`] = false : false +>options1[`foo`] : boolean +>options1 : { prop: number; } & { [k: string]: boolean; } +>`foo` : "foo" +>false : false + +options1[`foo/${path}`] = false; +>options1[`foo/${path}`] = false : false +>options1[`foo/${path}`] : ({ prop: number; } & { [k: string]: boolean; })[`foo/${Path}`] +>options1 : { prop: number; } & { [k: string]: boolean; } +>`foo/${path}` : `foo/${Path}` +>path : Path +>false : false + diff --git a/tests/cases/compiler/templateLiteralIntersection3.ts b/tests/cases/compiler/templateLiteralIntersection3.ts new file mode 100644 index 0000000000000..e05cbcc2a4968 --- /dev/null +++ b/tests/cases/compiler/templateLiteralIntersection3.ts @@ -0,0 +1,11 @@ +// @strict: true +// @noEmit: true + +type Path = string & { _pathBrand: any }; +declare const path: Path; + +declare const options1: { prop: number; } & { [k: string]: boolean; }; + +options1[`foo`] = false; + +options1[`foo/${path}`] = false; From 8d55d27b924f46c433fbe0f7b7d7f448043802b3 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sat, 18 Feb 2023 11:43:27 -0800 Subject: [PATCH 07/12] Add objectFlags to TemplateLiteral in order to propagate generic-ness --- src/compiler/checker.ts | 13 +++++++------ src/compiler/types.ts | 2 ++ .../templateLiteralIntersection3.errors.txt | 15 --------------- .../reference/templateLiteralIntersection3.types | 2 +- 4 files changed, 10 insertions(+), 22 deletions(-) delete mode 100644 tests/baselines/reference/templateLiteralIntersection3.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 40eb6533ae894..61ed6f3aee83b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17041,6 +17041,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function createTemplateLiteralType(texts: readonly string[], types: readonly Type[]) { const type = createType(TypeFlags.TemplateLiteral) as TemplateLiteralType; + type.objectFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); type.texts = texts; type.types = types; return type; @@ -17372,12 +17373,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getGenericObjectFlags(type: Type): ObjectFlags { - if (type.flags & TypeFlags.UnionOrIntersection) { - if (!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericTypeComputed)) { - (type as UnionOrIntersectionType).objectFlags |= ObjectFlags.IsGenericTypeComputed | - reduceLeft((type as UnionOrIntersectionType).types, (flags, t) => flags | getGenericObjectFlags(t), 0); + if (type.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral)) { + if (!((type as UnionOrIntersectionType | TemplateLiteralType).objectFlags & ObjectFlags.IsGenericTypeComputed)) { + (type as UnionOrIntersectionType | TemplateLiteralType).objectFlags |= ObjectFlags.IsGenericTypeComputed | + reduceLeft((type as UnionOrIntersectionType | TemplateLiteralType).types, (flags, t) => flags | getGenericObjectFlags(t), 0); } - return (type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericType; + return (type as UnionOrIntersectionType | TemplateLiteralType).objectFlags & ObjectFlags.IsGenericType; } if (type.flags & TypeFlags.Substitution) { if (!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericTypeComputed)) { @@ -17387,7 +17388,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return (type as SubstitutionType).objectFlags & ObjectFlags.IsGenericType; } return (type.flags & TypeFlags.InstantiableNonPrimitive || isGenericMappedType(type) || isGenericTupleType(type) ? ObjectFlags.IsGenericObjectType : 0) | - (type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) && !isPatternLiteralType(type) ? ObjectFlags.IsGenericIndexType : 0); + (type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.StringMapping) && !isPatternLiteralType(type) ? ObjectFlags.IsGenericIndexType : 0); } function getSimplifiedType(type: Type, writing: boolean): Type { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 38b3c767934df..27102c2a9f801 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6595,6 +6595,8 @@ export interface ConditionalType extends InstantiableType { } export interface TemplateLiteralType extends InstantiableType { + /** @internal */ + objectFlags: ObjectFlags; texts: readonly string[]; // Always one element longer than types types: readonly Type[]; // Always at least one element } diff --git a/tests/baselines/reference/templateLiteralIntersection3.errors.txt b/tests/baselines/reference/templateLiteralIntersection3.errors.txt deleted file mode 100644 index d13f87246fbf3..0000000000000 --- a/tests/baselines/reference/templateLiteralIntersection3.errors.txt +++ /dev/null @@ -1,15 +0,0 @@ -tests/cases/compiler/templateLiteralIntersection3.ts(8,1): error TS2322: Type 'boolean' is not assignable to type '({ prop: number; } & { [k: string]: boolean; })[`foo/${Path}`]'. - - -==== tests/cases/compiler/templateLiteralIntersection3.ts (1 errors) ==== - type Path = string & { _pathBrand: any }; - declare const path: Path; - - declare const options1: { prop: number; } & { [k: string]: boolean; }; - - options1[`foo`] = false; - - options1[`foo/${path}`] = false; - ~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2322: Type 'boolean' is not assignable to type '({ prop: number; } & { [k: string]: boolean; })[`foo/${Path}`]'. - \ No newline at end of file diff --git a/tests/baselines/reference/templateLiteralIntersection3.types b/tests/baselines/reference/templateLiteralIntersection3.types index 14c869b04d20e..f14d8ced181fd 100644 --- a/tests/baselines/reference/templateLiteralIntersection3.types +++ b/tests/baselines/reference/templateLiteralIntersection3.types @@ -20,7 +20,7 @@ options1[`foo`] = false; options1[`foo/${path}`] = false; >options1[`foo/${path}`] = false : false ->options1[`foo/${path}`] : ({ prop: number; } & { [k: string]: boolean; })[`foo/${Path}`] +>options1[`foo/${path}`] : boolean >options1 : { prop: number; } & { [k: string]: boolean; } >`foo/${path}` : `foo/${Path}` >path : Path From 0e048dba5bdebb4185fb74c12b8ba70a0d569826 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sat, 18 Feb 2023 12:01:23 -0800 Subject: [PATCH 08/12] Add new test, showing bad interaction of StringMapping --- .../reference/templateLiteralIntersection3.symbols | 11 +++++++++++ .../reference/templateLiteralIntersection3.types | 12 ++++++++++++ tests/cases/compiler/templateLiteralIntersection3.ts | 6 ++++++ 3 files changed, 29 insertions(+) diff --git a/tests/baselines/reference/templateLiteralIntersection3.symbols b/tests/baselines/reference/templateLiteralIntersection3.symbols index 05d7fec8333b5..1734daee2704d 100644 --- a/tests/baselines/reference/templateLiteralIntersection3.symbols +++ b/tests/baselines/reference/templateLiteralIntersection3.symbols @@ -19,3 +19,14 @@ options1[`foo/${path}`] = false; >options1 : Symbol(options1, Decl(templateLiteralIntersection3.ts, 3, 13)) >path : Symbol(path, Decl(templateLiteralIntersection3.ts, 1, 13)) + +// Lowercase<`foo/${Path}`> => `foo/${Lowercase}` +declare const lowercasePath: Lowercase<`foo/${Path}`>; +>lowercasePath : Symbol(lowercasePath, Decl(templateLiteralIntersection3.ts, 11, 13)) +>Lowercase : Symbol(Lowercase, Decl(lib.es5.d.ts, --, --)) +>Path : Symbol(Path, Decl(templateLiteralIntersection3.ts, 0, 0)) + +options1[lowercasePath] = false; +>options1 : Symbol(options1, Decl(templateLiteralIntersection3.ts, 3, 13)) +>lowercasePath : Symbol(lowercasePath, Decl(templateLiteralIntersection3.ts, 11, 13)) + diff --git a/tests/baselines/reference/templateLiteralIntersection3.types b/tests/baselines/reference/templateLiteralIntersection3.types index f14d8ced181fd..46a447d3b42a7 100644 --- a/tests/baselines/reference/templateLiteralIntersection3.types +++ b/tests/baselines/reference/templateLiteralIntersection3.types @@ -26,3 +26,15 @@ options1[`foo/${path}`] = false; >path : Path >false : false + +// Lowercase<`foo/${Path}`> => `foo/${Lowercase}` +declare const lowercasePath: Lowercase<`foo/${Path}`>; +>lowercasePath : `foo/${Path}` + +options1[lowercasePath] = false; +>options1[lowercasePath] = false : false +>options1[lowercasePath] : boolean +>options1 : { prop: number; } & { [k: string]: boolean; } +>lowercasePath : `foo/${Path}` +>false : false + diff --git a/tests/cases/compiler/templateLiteralIntersection3.ts b/tests/cases/compiler/templateLiteralIntersection3.ts index e05cbcc2a4968..a9d71693ef18a 100644 --- a/tests/cases/compiler/templateLiteralIntersection3.ts +++ b/tests/cases/compiler/templateLiteralIntersection3.ts @@ -9,3 +9,9 @@ declare const options1: { prop: number; } & { [k: string]: boolean; }; options1[`foo`] = false; options1[`foo/${path}`] = false; + + +// Lowercase<`foo/${Path}`> => `foo/${Lowercase}` +declare const lowercasePath: Lowercase<`foo/${Path}`>; + +options1[lowercasePath] = false; From 7d38709fe7e25f1456cc95893126b7d4b09dfd4a Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sat, 18 Feb 2023 12:16:18 -0800 Subject: [PATCH 09/12] Handle intersections in getStringMappingType --- src/compiler/checker.ts | 1 + tests/baselines/reference/templateLiteralIntersection3.types | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 61ed6f3aee83b..a28f1069be94e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17049,6 +17049,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getStringMappingType(symbol: Symbol, type: Type): Type { return type.flags & (TypeFlags.Union | TypeFlags.Never) ? mapType(type, t => getStringMappingType(symbol, t)) : + type.flags & TypeFlags.Intersection ? getIntersectionType(map((type as IntersectionType).types, t => getStringMappingType(symbol, t))) : type.flags & TypeFlags.StringLiteral ? getStringLiteralType(applyStringMapping(symbol, (type as StringLiteralType).value)) : type.flags & TypeFlags.TemplateLiteral ? getTemplateLiteralType(...applyTemplateStringMapping(symbol, (type as TemplateLiteralType).texts, (type as TemplateLiteralType).types)) : // Mapping> === Mapping diff --git a/tests/baselines/reference/templateLiteralIntersection3.types b/tests/baselines/reference/templateLiteralIntersection3.types index 46a447d3b42a7..4c89a8c82c2f7 100644 --- a/tests/baselines/reference/templateLiteralIntersection3.types +++ b/tests/baselines/reference/templateLiteralIntersection3.types @@ -29,12 +29,12 @@ options1[`foo/${path}`] = false; // Lowercase<`foo/${Path}`> => `foo/${Lowercase}` declare const lowercasePath: Lowercase<`foo/${Path}`>; ->lowercasePath : `foo/${Path}` +>lowercasePath : `foo/${Lowercase & { _pathBrand: any; }}` options1[lowercasePath] = false; >options1[lowercasePath] = false : false >options1[lowercasePath] : boolean >options1 : { prop: number; } & { [k: string]: boolean; } ->lowercasePath : `foo/${Path}` +>lowercasePath : `foo/${Lowercase & { _pathBrand: any; }}` >false : false From 84a7a10e01eed7de23b830695adccc9b960f1e20 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 2 Mar 2023 08:56:02 -0800 Subject: [PATCH 10/12] Add to ObjectFlagsType, but I'm not sure I like this --- src/compiler/checker.ts | 2 +- src/compiler/types.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a28f1069be94e..c58416765b437 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23808,7 +23808,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { objectFlags & ObjectFlags.Reference && ((type as TypeReference).node || forEach(getTypeArguments(type as TypeReference), couldContainTypeVariables)) || objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations || objectFlags & (ObjectFlags.Mapped | ObjectFlags.ReverseMapped | ObjectFlags.ObjectRestType | ObjectFlags.InstantiationExpressionType)) || - type.flags & TypeFlags.UnionOrIntersection && !(type.flags & TypeFlags.EnumLiteral) && !isNonGenericTopLevelType(type) && some((type as UnionOrIntersectionType).types, couldContainTypeVariables)); + type.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral) && !(type.flags & TypeFlags.EnumLiteral) && !isNonGenericTopLevelType(type) && some((type as UnionOrIntersectionType | TemplateLiteralType).types, couldContainTypeVariables)); if (type.flags & TypeFlags.ObjectFlagsType) { (type as ObjectFlagsType).objectFlags |= ObjectFlags.CouldContainTypeVariablesComputed | (result ? ObjectFlags.CouldContainTypeVariables : 0); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 27102c2a9f801..bfc433df02000 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6074,7 +6074,7 @@ export const enum TypeFlags { Instantiable = InstantiableNonPrimitive | InstantiablePrimitive, StructuredOrInstantiable = StructuredType | Instantiable, /** @internal */ - ObjectFlagsType = Any | Nullable | Never | Object | Union | Intersection, + ObjectFlagsType = Any | Nullable | Never | Object | Union | Intersection | TemplateLiteral, /** @internal */ Simplifiable = IndexedAccess | Conditional, /** @internal */ @@ -6230,7 +6230,7 @@ export const enum ObjectFlags { /** @internal */ IdenticalBaseTypeExists = 1 << 26, // has a defined cachedEquivalentBaseType member - // Flags that require TypeFlags.UnionOrIntersection or TypeFlags.Substitution + // Flags that require TypeFlags.UnionOrIntersection, TypeFlags.Substitution, or TypeFlags.TemplateLiteral /** @internal */ IsGenericTypeComputed = 1 << 21, // IsGenericObjectType flag has been computed /** @internal */ @@ -6257,7 +6257,7 @@ export const enum ObjectFlags { } /** @internal */ -export type ObjectFlagsType = NullableType | ObjectType | UnionType | IntersectionType; +export type ObjectFlagsType = NullableType | ObjectType | UnionType | IntersectionType | TemplateLiteralType; // Object types (TypeFlags.ObjectType) export interface ObjectType extends Type { From 026cd53f82152dffa4df6644a038e5a1cc4dea11 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 14 Mar 2023 16:53:32 -0700 Subject: [PATCH 11/12] PR feedback maybe --- src/compiler/checker.ts | 16 ++++++++++++++-- tests/baselines/reference/intrinsicTypes.types | 8 ++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6dc519f3c0fa5..9fb883dd042fb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17063,7 +17063,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!addSpans((t as TemplateLiteralType).texts, (t as TemplateLiteralType).types)) return false; text += texts[i + 1]; } - else if (isGenericIndexType(t) || isPatternLiteralPlaceholderType(t) || t.flags & TypeFlags.Intersection) { + else if (isGenericIndexType(t) || isPatternLiteralPlaceholderType(t)) { newTypes.push(t); newTexts.push(text); text = texts[i + 1]; @@ -17398,7 +17398,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function isPatternLiteralPlaceholderType(type: Type): boolean { - return !!(type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)) || isPatternLiteralType(type); + if (type.flags & TypeFlags.Intersection) { + return some((type as IntersectionType).types, isPatternLiteralPlaceholderType); + } + + if (type.flags & (TypeFlags.TemplateLiteral | TypeFlags.StringMapping)) { + return isPatternLiteralType(type); + } + + if (type.flags & TypeFlags.ESSymbolLike) { + return false; + } + + return !!(type.flags & (TypeFlags.Any | TypeFlags.Primitive)); } function isPatternLiteralType(type: Type) { diff --git a/tests/baselines/reference/intrinsicTypes.types b/tests/baselines/reference/intrinsicTypes.types index b054269683432..750d4c58c0d19 100644 --- a/tests/baselines/reference/intrinsicTypes.types +++ b/tests/baselines/reference/intrinsicTypes.types @@ -15,7 +15,7 @@ type TU5 = Uppercase; // never >TU5 : never type TU6 = Uppercase<42>; // Error ->TU6 : 42 +>TU6 : Uppercase<"42"> type TL1 = Lowercase<'HELLO'>; // "hello" >TL1 : "hello" @@ -33,7 +33,7 @@ type TL5 = Lowercase; // never >TL5 : never type TL6 = Lowercase<42>; // Error ->TL6 : 42 +>TL6 : Lowercase<"42"> type TC1 = Capitalize<'hello'>; // "Hello" >TC1 : "Hello" @@ -51,7 +51,7 @@ type TC5 = Capitalize; // never >TC5 : never type TC6 = Capitalize<42>; // Error ->TC6 : 42 +>TC6 : Capitalize<"42"> type TN1 = Uncapitalize<'Hello'>; // "hello" >TN1 : "hello" @@ -69,7 +69,7 @@ type TN5 = Uncapitalize; // never >TN5 : never type TN6 = Uncapitalize<42>; // Error ->TN6 : 42 +>TN6 : Uncapitalize<"42"> type TX1 = Uppercase<`aB${S}`>; >TX1 : `AB${Uppercase}` From 15e3676f2e8c4a47d8a10e262f7c1bd51bb9f447 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 16 Mar 2023 10:54:24 -0700 Subject: [PATCH 12/12] PR feedback --- src/compiler/checker.ts | 13 ++----------- tests/baselines/reference/intrinsicTypes.types | 8 ++++---- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3dace816961c3..29804b039ceb9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17422,18 +17422,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function isPatternLiteralPlaceholderType(type: Type): boolean { if (type.flags & TypeFlags.Intersection) { - return some((type as IntersectionType).types, isPatternLiteralPlaceholderType); + return some((type as IntersectionType).types, t => !!(t.flags & (TypeFlags.Literal | TypeFlags.Null | TypeFlags.Undefined)) || isPatternLiteralPlaceholderType(t)); } - - if (type.flags & (TypeFlags.TemplateLiteral | TypeFlags.StringMapping)) { - return isPatternLiteralType(type); - } - - if (type.flags & TypeFlags.ESSymbolLike) { - return false; - } - - return !!(type.flags & (TypeFlags.Any | TypeFlags.Primitive)); + return !!(type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)) || isPatternLiteralType(type); } function isPatternLiteralType(type: Type) { diff --git a/tests/baselines/reference/intrinsicTypes.types b/tests/baselines/reference/intrinsicTypes.types index 750d4c58c0d19..b054269683432 100644 --- a/tests/baselines/reference/intrinsicTypes.types +++ b/tests/baselines/reference/intrinsicTypes.types @@ -15,7 +15,7 @@ type TU5 = Uppercase; // never >TU5 : never type TU6 = Uppercase<42>; // Error ->TU6 : Uppercase<"42"> +>TU6 : 42 type TL1 = Lowercase<'HELLO'>; // "hello" >TL1 : "hello" @@ -33,7 +33,7 @@ type TL5 = Lowercase; // never >TL5 : never type TL6 = Lowercase<42>; // Error ->TL6 : Lowercase<"42"> +>TL6 : 42 type TC1 = Capitalize<'hello'>; // "Hello" >TC1 : "Hello" @@ -51,7 +51,7 @@ type TC5 = Capitalize; // never >TC5 : never type TC6 = Capitalize<42>; // Error ->TC6 : Capitalize<"42"> +>TC6 : 42 type TN1 = Uncapitalize<'Hello'>; // "hello" >TN1 : "hello" @@ -69,7 +69,7 @@ type TN5 = Uncapitalize; // never >TN5 : never type TN6 = Uncapitalize<42>; // Error ->TN6 : Uncapitalize<"42"> +>TN6 : 42 type TX1 = Uppercase<`aB${S}`>; >TX1 : `AB${Uppercase}`