microsoft / microsoft/TypeScript
Excess-property check fires on a nested object literal that is later assigned to an annotated return type, where tsc 6.0 does not
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.3k
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 132
Description
## Steps to reproduce
`tsconfig.json`:
```json
{
"compilerOptions": {
"strict": true,
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"noEmit": true
}
}
```
`index.ts`:
```ts
interface TypeData {
ref?: string;
}
interface UsageData {
Type_Data?: Array;
Comments?: string;
}
interface EmailAddressData {
Email_Address: string;
Usage_Data?: Array;
}
declare function condition(): boolean;
declare function makeRef(s: string): string;
declare const emails: { value: string; isWork: boolean }[];
async function build(): Promise {
let emailAddressData;
if (condition()) {
const primary = emails[0];
emailAddressData = primary
? [
{
Email_Address: primary.value,
Usage_Data: [
{
attributes: { "wd:Public": false },
Type_Data: [
{
attributes: { "wd:Primary": true },
ref: makeRef("HOME"),
},
],
},
],
},
]
: [];
} else {
emailAddressData = emails.map((email) => ({
Email_Address: email.value,
Usage_Data: [
{
attributes: { "wd:Public": email.isWork },
Type_Data: [
{
attributes: { "wd:Primary": true },
ref: makeRef("HOME"),
},
],
},
],
}));
}
return emailAddressData;
}
build().then(console.log);
```
Tested with `@typescript/native-preview@beta` (`Version 7.0.0-dev.20260421.2`) and `typescript@6.0.3`.
The `attributes` field doesn't exist on `UsageData` or `TypeData` — but neither does it exist in any of the literals' contextual type at the *literal* expression sites, because both branches assign to the same untyped `let` and the contextual type only kicks in at the `return` statement.
## Behavior with `typescript@6.0`
Compiles cleanly (exit 0, no diagnostics). Excess-property checking does not fire on the nested literals because they are assigned to an untyped `let` variable and the contextual type only flows in at `return emailAddressData;` (where excess-prop checking is no longer "fresh").
## Behavior with `tsgo`
```
index.ts(29,17): error TS2353: Object literal may only specify known properties, and 'attributes' does not exist in type 'UsageData'.
```
Note that:
- Only the first `attributes: { "wd:Public": false }` (the `if (condition())` branch) is flagged. The matching `attributes` field in the `else` branch is **not** flagged.
- The inner `attributes: { "wd:Primary": true }` on `TypeData` is **not** flagged in either branch.
That asymmetric reporting suggests tsgo is propagating the contextual type back into one of the conditional branches (the `primary ? [...] : []` ternary that's inside the if-true branch) but not into `emails.map((email) => ({...}))` in the else branch. tsc treats both branches consistently.
Contributor guide
Assessment
This issue has not been assessed yet.