Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Branded literal type widened in unexpected way #61093

Open
chkn opened this issue Jan 31, 2025 · 0 comments
Open

Branded literal type widened in unexpected way #61093

chkn opened this issue Jan 31, 2025 · 0 comments
Labels
Bug A bug in TypeScript Help Wanted You can do this
Milestone

Comments

@chkn
Copy link

chkn commented Jan 31, 2025

🔎 Search Terms

literal intersection branded string

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about branded strings/literal intersections.

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.7.3#code/CYUwxgNghgTiAEYD2A7AzgF3hgngBxAGkQcAueAVxQEsBHChNHAWwCMkIBuAKG9wPgAVfCACSAEQA8wgQF5KKANYokAdxQAaeBPggAHhhApgaeJhjUUAc3jzzlqwD5b28fABk8AN7wA2vyISAF0AfnIZBABfHm4AMyowDGpUbBEJaREtHX1DY1N7a0cACmpgcgkASnC0qQis8Wcvbnh4OAwKGBR4Up5I3gD4YhwAeVjpbIMjEyEa53lBCdzpiPSoFBwtS1iQGFdnENd4chQQADcdmIGI0fG3HKnTFYaXBbvJvJmCdK2dz5B9v5HeAnc4wS4ieAABRgSFOpRAaEkkN072WsxcPjgUGAqAgOD8gm6XSGN0hjiC1QIpOc0V4AHo6fAALQssAUDAspm8SyGGCxKBgBAAMSQSG8zRa8FioqKVXgpyQPW4fW4yHQWBFYvkAQkRQARJq9RUzFAkmhYtQEX90prHDwGZL4AA9EK8NWYeAAIVg5GhsPhiICSFi8FtGIlLV8mop4sdjulSFl3j6jr60SAA

💻 Code

declare const typeKey: unique symbol;

type TypeID<Type = unknown, ID extends string = string> = ID & { [typeKey]?: Type };

function typeID<Type, ID extends string>(id: ID): TypeID<Type, ID> {
  return id;
}

type KeyOf<TID extends TypeID> = TID extends TypeID<any, infer ID> ? ID : never;

type TypeOf<TID extends TypeID> = TID extends TypeID<infer Type> ? Type : never;

type Provides<P extends TypeID> = { readonly [T in KeyOf<P>]: TypeOf<P> };

// ---cut---

interface Foo {
    foo(): void;
}

const Foo = typeID("Foo") satisfies TypeID<Foo>;
//    ^? const Foo: TypeID<Foo, "Foo">

const Bar: Provides<typeof Foo> = {
    [Foo]: {
        foo() {}
    }
};

🙁 Actual behavior

Fails to compile with error:

Property 'Foo' is missing in type '{ [x: string]: { foo(): void; }; }' but required in type 'Provides<TypeID<Foo, "Foo">>'.

🙂 Expected behavior

I'd expect it to compile. Rather than widening the literal type to string, I'd expect it to discard the { [typeKey]?: Type } part, since that's pretty much bogus at runtime anyway.

However, it should also not lose the type information for the generic types. For instance, if I change the declaration to...

type TypeID<Type = unknown, ID extends string = string> = ID;

...then it does compile, but so does this...

const Bar: Provides<typeof Foo> = {
    [Foo]: {
        // No error about missing member `foo` here!
    }
};

Additional information about the issue

Possibly related to #43852 (comment)

@RyanCavanaugh RyanCavanaugh added Bug A bug in TypeScript Help Wanted You can do this labels Jan 31, 2025
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Jan 31, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Help Wanted You can do this
Projects
None yet
Development

No branches or pull requests

2 participants