diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 090f8dcd50e0d..74812e260d26e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22913,6 +22913,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } + if (source.flags & TypeFlags.TemplateLiteral) { + if (target.flags & TypeFlags.StringLike) { + const resolvedPrimitiveTypes = (source as TemplateLiteralType).types.flatMap(type => { + if (type.flags & TypeFlags.Intersection && (type as IntersectionType).types.every(type => type.flags & TypeFlags.Primitive || type.flags & TypeFlags.Object)) { + return (type as IntersectionType).types.filter(t => t.flags & TypeFlags.Primitive); + } + else { + return [type]; + } + }); + + (source as TemplateLiteralType).types = resolvedPrimitiveTypes; + } + } + // We limit alias variance probing to only object and conditional types since their alias behavior // is more predictable than other, interned types, which may or may not have an alias depending on // the order in which things were checked. diff --git a/tests/baselines/reference/brandedLiteralRevert1.symbols b/tests/baselines/reference/brandedLiteralRevert1.symbols new file mode 100644 index 0000000000000..ba7e9ed9ecff8 --- /dev/null +++ b/tests/baselines/reference/brandedLiteralRevert1.symbols @@ -0,0 +1,35 @@ +//// [tests/cases/compiler/brandedLiteralRevert1.ts] //// + +=== brandedLiteralRevert1.ts === +// https://github.com/microsoft/TypeScript/issues/60990 + +type N0 = number & { +>N0 : Symbol(N0, Decl(brandedLiteralRevert1.ts, 0, 0)) + + test: "TEST" +>test : Symbol(test, Decl(brandedLiteralRevert1.ts, 2, 20)) +} + +type N1 = 3 & { +>N1 : Symbol(N1, Decl(brandedLiteralRevert1.ts, 4, 1)) + + test: "TEST" +>test : Symbol(test, Decl(brandedLiteralRevert1.ts, 6, 15)) +} + +declare let n0: N0 +>n0 : Symbol(n0, Decl(brandedLiteralRevert1.ts, 10, 11)) +>N0 : Symbol(N0, Decl(brandedLiteralRevert1.ts, 0, 0)) + +declare let n1: N1 +>n1 : Symbol(n1, Decl(brandedLiteralRevert1.ts, 11, 11)) +>N1 : Symbol(N1, Decl(brandedLiteralRevert1.ts, 4, 1)) + +let m0: `${number}` = `${n0}` // ok +>m0 : Symbol(m0, Decl(brandedLiteralRevert1.ts, 13, 3)) +>n0 : Symbol(n0, Decl(brandedLiteralRevert1.ts, 10, 11)) + +let m1: "3" = `${n1}` // ok +>m1 : Symbol(m1, Decl(brandedLiteralRevert1.ts, 14, 3)) +>n1 : Symbol(n1, Decl(brandedLiteralRevert1.ts, 11, 11)) + diff --git a/tests/baselines/reference/brandedLiteralRevert1.types b/tests/baselines/reference/brandedLiteralRevert1.types new file mode 100644 index 0000000000000..a31b1847b02d8 --- /dev/null +++ b/tests/baselines/reference/brandedLiteralRevert1.types @@ -0,0 +1,47 @@ +//// [tests/cases/compiler/brandedLiteralRevert1.ts] //// + +=== brandedLiteralRevert1.ts === +// https://github.com/microsoft/TypeScript/issues/60990 + +type N0 = number & { +>N0 : N0 +> : ^^ + + test: "TEST" +>test : "TEST" +> : ^^^^^^ +} + +type N1 = 3 & { +>N1 : N1 +> : ^^ + + test: "TEST" +>test : "TEST" +> : ^^^^^^ +} + +declare let n0: N0 +>n0 : N0 +> : ^^ + +declare let n1: N1 +>n1 : N1 +> : ^^ + +let m0: `${number}` = `${n0}` // ok +>m0 : `${number}` +> : ^^^^^^^^^^^ +>`${n0}` : `${number}` +> : ^^^^^^^^^^^ +>n0 : N0 +> : ^^ + +let m1: "3" = `${n1}` // ok +>m1 : "3" +> : ^^^ +>`${n1}` : `${3}` +> : ^^^^^^ +>n1 : N1 +> : ^^ + diff --git a/tests/baselines/reference/brandedLiteralRevert2.symbols b/tests/baselines/reference/brandedLiteralRevert2.symbols new file mode 100644 index 0000000000000..f63816d80b2bf --- /dev/null +++ b/tests/baselines/reference/brandedLiteralRevert2.symbols @@ -0,0 +1,50 @@ +//// [tests/cases/compiler/brandedLiteralRevert2.ts] //// + +=== brandedLiteralRevert2.ts === +// https://github.com/microsoft/TypeScript/issues/60990 + +type Brand1 = number & 1 & { +>Brand1 : Symbol(Brand1, Decl(brandedLiteralRevert2.ts, 0, 0)) + + test: "TEST" +>test : Symbol(test, Decl(brandedLiteralRevert2.ts, 2, 28)) +} + +type Brand2 = "a" & { +>Brand2 : Symbol(Brand2, Decl(brandedLiteralRevert2.ts, 4, 1)) + + test: "TEST1" +>test : Symbol(test, Decl(brandedLiteralRevert2.ts, 6, 21)) +} + +let brandN1: Brand1 = 1 as Brand1 +>brandN1 : Symbol(brandN1, Decl(brandedLiteralRevert2.ts, 10, 3)) +>Brand1 : Symbol(Brand1, Decl(brandedLiteralRevert2.ts, 0, 0)) +>Brand1 : Symbol(Brand1, Decl(brandedLiteralRevert2.ts, 0, 0)) + +let brandN2: Brand2 = "a" as Brand2 +>brandN2 : Symbol(brandN2, Decl(brandedLiteralRevert2.ts, 11, 3)) +>Brand2 : Symbol(Brand2, Decl(brandedLiteralRevert2.ts, 4, 1)) +>Brand2 : Symbol(Brand2, Decl(brandedLiteralRevert2.ts, 4, 1)) + +let brandM11: `${number}` = `${brandN1}` // ok +>brandM11 : Symbol(brandM11, Decl(brandedLiteralRevert2.ts, 13, 3)) +>brandN1 : Symbol(brandN1, Decl(brandedLiteralRevert2.ts, 10, 3)) + +let brandM12: "1" = `${brandN1}` // ok +>brandM12 : Symbol(brandM12, Decl(brandedLiteralRevert2.ts, 14, 3)) +>brandN1 : Symbol(brandN1, Decl(brandedLiteralRevert2.ts, 10, 3)) + +let brandM13: `${1}` = `${brandN1}` // ok +>brandM13 : Symbol(brandM13, Decl(brandedLiteralRevert2.ts, 15, 3)) +>brandN1 : Symbol(brandN1, Decl(brandedLiteralRevert2.ts, 10, 3)) + +let brandM21: "a" = `${brandN2}` // ok +>brandM21 : Symbol(brandM21, Decl(brandedLiteralRevert2.ts, 17, 3)) +>brandN2 : Symbol(brandN2, Decl(brandedLiteralRevert2.ts, 11, 3)) + +let brandM22: "1a" = `${brandN1}${brandN2}` // ok +>brandM22 : Symbol(brandM22, Decl(brandedLiteralRevert2.ts, 18, 3)) +>brandN1 : Symbol(brandN1, Decl(brandedLiteralRevert2.ts, 10, 3)) +>brandN2 : Symbol(brandN2, Decl(brandedLiteralRevert2.ts, 11, 3)) + diff --git a/tests/baselines/reference/brandedLiteralRevert2.types b/tests/baselines/reference/brandedLiteralRevert2.types new file mode 100644 index 0000000000000..210ff480d55f9 --- /dev/null +++ b/tests/baselines/reference/brandedLiteralRevert2.types @@ -0,0 +1,81 @@ +//// [tests/cases/compiler/brandedLiteralRevert2.ts] //// + +=== brandedLiteralRevert2.ts === +// https://github.com/microsoft/TypeScript/issues/60990 + +type Brand1 = number & 1 & { +>Brand1 : Brand1 +> : ^^^^^^ + + test: "TEST" +>test : "TEST" +> : ^^^^^^ +} + +type Brand2 = "a" & { +>Brand2 : Brand2 +> : ^^^^^^ + + test: "TEST1" +>test : "TEST1" +> : ^^^^^^^ +} + +let brandN1: Brand1 = 1 as Brand1 +>brandN1 : Brand1 +> : ^^^^^^ +>1 as Brand1 : Brand1 +> : ^^^^^^ +>1 : 1 +> : ^ + +let brandN2: Brand2 = "a" as Brand2 +>brandN2 : Brand2 +> : ^^^^^^ +>"a" as Brand2 : Brand2 +> : ^^^^^^ +>"a" : "a" +> : ^^^ + +let brandM11: `${number}` = `${brandN1}` // ok +>brandM11 : `${number}` +> : ^^^^^^^^^^^ +>`${brandN1}` : `${1}` +> : ^^^^^^ +>brandN1 : Brand1 +> : ^^^^^^ + +let brandM12: "1" = `${brandN1}` // ok +>brandM12 : "1" +> : ^^^ +>`${brandN1}` : `${1}` +> : ^^^^^^ +>brandN1 : Brand1 +> : ^^^^^^ + +let brandM13: `${1}` = `${brandN1}` // ok +>brandM13 : "1" +> : ^^^ +>`${brandN1}` : `${1}` +> : ^^^^^^ +>brandN1 : Brand1 +> : ^^^^^^ + +let brandM21: "a" = `${brandN2}` // ok +>brandM21 : "a" +> : ^^^ +>`${brandN2}` : `${"a"}` +> : ^^^^^^^^ +>brandN2 : Brand2 +> : ^^^^^^ + +let brandM22: "1a" = `${brandN1}${brandN2}` // ok +>brandM22 : "1a" +> : ^^^^ +>`${brandN1}${brandN2}` : `${1}${"a"}` +> : ^^^^^^^^^^^^ +>brandN1 : Brand1 +> : ^^^^^^ +>brandN2 : Brand2 +> : ^^^^^^ + diff --git a/tests/cases/compiler/brandedLiteralRevert1.ts b/tests/cases/compiler/brandedLiteralRevert1.ts new file mode 100644 index 0000000000000..bcd44a60d7492 --- /dev/null +++ b/tests/cases/compiler/brandedLiteralRevert1.ts @@ -0,0 +1,18 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/60990 + +type N0 = number & { + test: "TEST" +} + +type N1 = 3 & { + test: "TEST" +} + +declare let n0: N0 +declare let n1: N1 + +let m0: `${number}` = `${n0}` // ok +let m1: "3" = `${n1}` // ok \ No newline at end of file diff --git a/tests/cases/compiler/brandedLiteralRevert2.ts b/tests/cases/compiler/brandedLiteralRevert2.ts new file mode 100644 index 0000000000000..562b661a41b51 --- /dev/null +++ b/tests/cases/compiler/brandedLiteralRevert2.ts @@ -0,0 +1,22 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/60990 + +type Brand1 = number & 1 & { + test: "TEST" +} + +type Brand2 = "a" & { + test: "TEST1" +} + +let brandN1: Brand1 = 1 as Brand1 +let brandN2: Brand2 = "a" as Brand2 + +let brandM11: `${number}` = `${brandN1}` // ok +let brandM12: "1" = `${brandN1}` // ok +let brandM13: `${1}` = `${brandN1}` // ok + +let brandM21: "a" = `${brandN2}` // ok +let brandM22: "1a" = `${brandN1}${brandN2}` // ok