microsoft / microsoft/TypeScript
Branded literal type widened in unexpected way
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.3k
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 132
Description
### 🔎 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
```ts
declare const typeKey: unique symbol;
type TypeID = ID & { [typeKey]?: Type };
function typeID(id: ID): TypeID {
return id;
}
type KeyOf = TID extends TypeID ? ID : never;
type TypeOf = TID extends TypeID ? Type : never;
type Provides
= { readonly [T in KeyOf
]: TypeOf
};
// ---cut---
interface Foo {
foo(): void;
}
const Foo = typeID("Foo") satisfies TypeID;
// ^? const Foo: TypeID
const Bar: Provides = {
[Foo]: {
foo() {}
}
};
```
### 🙁 Actual behavior
Fails to compile with error:
```
Property 'Foo' is missing in type '{ [x: string]: { foo(): void; }; }' but required in type 'Provides>'.
```
### 🙂 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...
```ts
type TypeID = ID;
```
...then it does compile, but so does this...
```ts
const Bar: Provides = {
[Foo]: {
// No error about missing member `foo` here!
}
};
```
### Additional information about the issue
Possibly related to https://github.com/microsoft/TypeScript/issues/43852#issuecomment-1671115898
Contributor guide
Research direction
Start with the TypeScript Playground reproduction and compare the branded-literal behavior with the related discussion in issue #43852. Trace the type-checking path for the `TypeID`, `KeyOf`, `TypeOf`, and `Provides` definitions; done means the example compiles while still reporting a missing `foo` member when appropriate.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- compilers, devtools
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100