diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e369ab69a0f9e..0753ebbc38885 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15256,24 +15256,32 @@ namespace ts { } return type; - function addSpans(texts: readonly string[], types: readonly Type[]): boolean { + function addSpans(texts: readonly string[] | string, types: readonly Type[]): boolean { + const isTextsArray = isArray(texts); 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 += texts[i + 1]; + text += addText; + if (!isTextsArray) return true; } 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 += texts[i + 1]; + text += addText; + if (!isTextsArray) return true; } else if (isGenericIndexType(t) || isPatternLiteralPlaceholderType(t)) { newTypes.push(t); newTexts.push(text); - text = texts[i + 1]; + text = addText; } - else { + else if (t.flags & TypeFlags.Intersection) { + const added = addSpans(texts[i + 1], (t as IntersectionType).types); + if (!added) return false; + } + else if (isTextsArray) { return false; } } diff --git a/tests/baselines/reference/templateLiteralIntersection.js b/tests/baselines/reference/templateLiteralIntersection.js new file mode 100644 index 0000000000000..24a5a684a2e0f --- /dev/null +++ b/tests/baselines/reference/templateLiteralIntersection.js @@ -0,0 +1,33 @@ +//// [templateLiteralIntersection.ts] +// https://github.com/microsoft/TypeScript/issues/48034 +const a = 'a' + +type A = typeof a +type MixA = A & {foo: string} + +type OriginA1 = `${A}` +type OriginA2 = `${MixA}` + +type B = `${typeof a}` +type MixB = B & { foo: string } + +type OriginB1 = `${B}` +type OriginB2 = `${MixB}` + +type MixC = { foo: string } & A + +type OriginC = `${MixC}` + +type MixD = + `${T & { foo: string }}` +type OriginD = `${MixD & { foo: string }}`; + +type E = `${A & {}}`; +type MixE = E & {} +type OriginE = `${MixE}` + +type OriginF = `${A}foo${A}`; + +//// [templateLiteralIntersection.js] +// https://github.com/microsoft/TypeScript/issues/48034 +var a = 'a'; diff --git a/tests/baselines/reference/templateLiteralIntersection.symbols b/tests/baselines/reference/templateLiteralIntersection.symbols new file mode 100644 index 0000000000000..031b081d203db --- /dev/null +++ b/tests/baselines/reference/templateLiteralIntersection.symbols @@ -0,0 +1,80 @@ +=== tests/cases/compiler/templateLiteralIntersection.ts === +// https://github.com/microsoft/TypeScript/issues/48034 +const a = 'a' +>a : Symbol(a, Decl(templateLiteralIntersection.ts, 1, 5)) + +type A = typeof a +>A : Symbol(A, Decl(templateLiteralIntersection.ts, 1, 13)) +>a : Symbol(a, Decl(templateLiteralIntersection.ts, 1, 5)) + +type MixA = A & {foo: string} +>MixA : Symbol(MixA, Decl(templateLiteralIntersection.ts, 3, 17)) +>A : Symbol(A, Decl(templateLiteralIntersection.ts, 1, 13)) +>foo : Symbol(foo, Decl(templateLiteralIntersection.ts, 4, 17)) + +type OriginA1 = `${A}` +>OriginA1 : Symbol(OriginA1, Decl(templateLiteralIntersection.ts, 4, 29)) +>A : Symbol(A, Decl(templateLiteralIntersection.ts, 1, 13)) + +type OriginA2 = `${MixA}` +>OriginA2 : Symbol(OriginA2, Decl(templateLiteralIntersection.ts, 6, 22)) +>MixA : Symbol(MixA, Decl(templateLiteralIntersection.ts, 3, 17)) + +type B = `${typeof a}` +>B : Symbol(B, Decl(templateLiteralIntersection.ts, 7, 25)) +>a : Symbol(a, Decl(templateLiteralIntersection.ts, 1, 5)) + +type MixB = B & { foo: string } +>MixB : Symbol(MixB, Decl(templateLiteralIntersection.ts, 9, 22)) +>B : Symbol(B, Decl(templateLiteralIntersection.ts, 7, 25)) +>foo : Symbol(foo, Decl(templateLiteralIntersection.ts, 10, 17)) + +type OriginB1 = `${B}` +>OriginB1 : Symbol(OriginB1, Decl(templateLiteralIntersection.ts, 10, 31)) +>B : Symbol(B, Decl(templateLiteralIntersection.ts, 7, 25)) + +type OriginB2 = `${MixB}` +>OriginB2 : Symbol(OriginB2, Decl(templateLiteralIntersection.ts, 12, 22)) +>MixB : Symbol(MixB, Decl(templateLiteralIntersection.ts, 9, 22)) + +type MixC = { foo: string } & A +>MixC : Symbol(MixC, Decl(templateLiteralIntersection.ts, 13, 25)) +>foo : Symbol(foo, Decl(templateLiteralIntersection.ts, 15, 13)) +>A : Symbol(A, Decl(templateLiteralIntersection.ts, 1, 13)) + +type OriginC = `${MixC}` +>OriginC : Symbol(OriginC, Decl(templateLiteralIntersection.ts, 15, 31)) +>MixC : Symbol(MixC, Decl(templateLiteralIntersection.ts, 13, 25)) + +type MixD = +>MixD : Symbol(MixD, Decl(templateLiteralIntersection.ts, 17, 24)) +>T : Symbol(T, Decl(templateLiteralIntersection.ts, 19, 10)) + + `${T & { foo: string }}` +>T : Symbol(T, Decl(templateLiteralIntersection.ts, 19, 10)) +>foo : Symbol(foo, Decl(templateLiteralIntersection.ts, 20, 12)) + +type OriginD = `${MixD & { foo: string }}`; +>OriginD : Symbol(OriginD, Decl(templateLiteralIntersection.ts, 20, 28)) +>MixD : Symbol(MixD, Decl(templateLiteralIntersection.ts, 17, 24)) +>A : Symbol(A, Decl(templateLiteralIntersection.ts, 1, 13)) +>foo : Symbol(foo, Decl(templateLiteralIntersection.ts, 21, 28)) +>foo : Symbol(foo, Decl(templateLiteralIntersection.ts, 21, 47)) + +type E = `${A & {}}`; +>E : Symbol(E, Decl(templateLiteralIntersection.ts, 21, 64)) +>A : Symbol(A, Decl(templateLiteralIntersection.ts, 1, 13)) + +type MixE = E & {} +>MixE : Symbol(MixE, Decl(templateLiteralIntersection.ts, 23, 21)) +>E : Symbol(E, Decl(templateLiteralIntersection.ts, 21, 64)) + +type OriginE = `${MixE}` +>OriginE : Symbol(OriginE, Decl(templateLiteralIntersection.ts, 24, 18)) +>MixE : Symbol(MixE, Decl(templateLiteralIntersection.ts, 23, 21)) + +type OriginF = `${A}foo${A}`; +>OriginF : Symbol(OriginF, Decl(templateLiteralIntersection.ts, 25, 24)) +>A : Symbol(A, Decl(templateLiteralIntersection.ts, 1, 13)) +>A : Symbol(A, Decl(templateLiteralIntersection.ts, 1, 13)) + diff --git a/tests/baselines/reference/templateLiteralIntersection.types b/tests/baselines/reference/templateLiteralIntersection.types new file mode 100644 index 0000000000000..d675ad0e0eb74 --- /dev/null +++ b/tests/baselines/reference/templateLiteralIntersection.types @@ -0,0 +1,64 @@ +=== tests/cases/compiler/templateLiteralIntersection.ts === +// https://github.com/microsoft/TypeScript/issues/48034 +const a = 'a' +>a : "a" +>'a' : "a" + +type A = typeof a +>A : "a" +>a : "a" + +type MixA = A & {foo: string} +>MixA : MixA +>foo : string + +type OriginA1 = `${A}` +>OriginA1 : "a" + +type OriginA2 = `${MixA}` +>OriginA2 : "a" + +type B = `${typeof a}` +>B : "a" +>a : "a" + +type MixB = B & { foo: string } +>MixB : MixB +>foo : string + +type OriginB1 = `${B}` +>OriginB1 : "a" + +type OriginB2 = `${MixB}` +>OriginB2 : "a" + +type MixC = { foo: string } & A +>MixC : MixC +>foo : string + +type OriginC = `${MixC}` +>OriginC : "a" + +type MixD = +>MixD : `${T & { foo: string; }}` + + `${T & { foo: string }}` +>foo : string + +type OriginD = `${MixD & { foo: string }}`; +>OriginD : "a" +>foo : string +>foo : string + +type E = `${A & {}}`; +>E : "a" + +type MixE = E & {} +>MixE : MixE + +type OriginE = `${MixE}` +>OriginE : "a" + +type OriginF = `${A}foo${A}`; +>OriginF : "afooa" + diff --git a/tests/cases/compiler/templateLiteralIntersection.ts b/tests/cases/compiler/templateLiteralIntersection.ts new file mode 100644 index 0000000000000..9a17d16bc2b2f --- /dev/null +++ b/tests/cases/compiler/templateLiteralIntersection.ts @@ -0,0 +1,28 @@ +// https://github.com/microsoft/TypeScript/issues/48034 +const a = 'a' + +type A = typeof a +type MixA = A & {foo: string} + +type OriginA1 = `${A}` +type OriginA2 = `${MixA}` + +type B = `${typeof a}` +type MixB = B & { foo: string } + +type OriginB1 = `${B}` +type OriginB2 = `${MixB}` + +type MixC = { foo: string } & A + +type OriginC = `${MixC}` + +type MixD = + `${T & { foo: string }}` +type OriginD = `${MixD & { foo: string }}`; + +type E = `${A & {}}`; +type MixE = E & {} +type OriginE = `${MixE}` + +type OriginF = `${A}foo${A}`; \ No newline at end of file