Skip to content

union of pattern template literals intersected with optional brand mutually assignable to one with incompatible brand #63420

@jcalz

Description

@jcalz

🔎 Search Terms

brand, flavor, pattern template literal, placeholder, union, intersection, optional, assignable

🕗 Version & Regression Information

⏯ Playground Link

Playground link

💻 Code

type TmpLitUnion = `0${string}` | `1${string}`
type BrandX = TmpLitUnion & { a?: "x" };
type BrandY = TmpLitUnion & { a?: "y" };
declare const brandY: BrandY;
const brandX: BrandX = brandY; // <-- no error

🙁 Actual behavior

BrandX and BrandY are mutually assignable, even though they have an incompatible optional a property.

🙂 Expected behavior

There should be an error when trying to assign a BrandY to a BrandX or vice versa.

Additional information about the issue

This is from a Stack Overflow question. Looks like optional brands (this is called... "flavored" I guess?) don't distinguish between unions-of-pattern-template-literals. If you get rid of the union, you get the expected error:

type TmpLit = `0${string}`;
type BrandX = TmpLit & { a?: "x" };
type BrandY = TmpLit & { a?: "y" };
declare const brandY: BrandY;
const brandX: BrandX = brandY; // <-- error

Or if you add something else to the union, you get the expected error:

type TmpLitUnionOrSomethingElse = `0${string}` | `1${string}` | "somethingElse"
type BrandX = TmpLitUnionOrSomethingElse & { a?: "x" };
type BrandY = TmpLitUnionOrSomethingElse & { a?: "y" };
declare const brandY: BrandY;
const brandX: BrandX = brandY; // <-- error

Or if the brand is not optional, etc.

Not sure if this is a bug or a design limitation, or somehow intentional.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScriptDomain: IntersectionIncorrect behavior under `T & U` constructs

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions