microsoft / microsoft/TypeScript

Branded literal type widened in unexpected way

Open
#61,093 0 comments 0 reactions 0 assignees View on GitHub
Bug Domain: Intersection Help Wanted
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.