Repository: TypeScript
Issue: Allow extra fields in object literals if they are used in methods on the object
Labels: Suggestion,Awaiting More Feedback

# Suggestion

There are two issues with the type inference applied to object literals with methods in argument position:
1. Methods have their `this` type implicitly set to the inferred parameter type, rather than the actual type of the object literal (as they do in other contexts)
2. Additional fields are flagged as errors even if they are used by the methods

The default behavior of not allowing unknown fields in object literals is useful in general because it detects typos in optional field names. However, it can be a problem if you want to add extra private(ish) data shared between methods on an object.  I think that TS should not consider extra fields as "unknown" if they are used by methods on the object.

There are currently two workarounds (as shown in the code sample and playground link below): 1) pull the object literal out to a named const, or 2) make the function that accepts the object a template rather than giving it the exact interface type. These both have the downside that they completely turn off unknown field checking, so you can still get typos in optional parameters. I think that an ideal solution would still only permit "known" fields, it should just expand the definition of what fields are known.


## 🔍 Search Terms

<!--
  💡 Did you know? TypeScript has over 2,000 open suggestions!
  🔎 Please search thoroughly before logging new feature requests as most common ideas already have a proposal in progress.
  The "Common Feature Requests" section of the FAQ lists many popular requests: https://github.com/Microsoft/TypeScript/wiki/FAQ#common-feature-requests

  Replace the text below:
-->

I searched for "object literal methods" and poked around a bit, but didn't see anything like this. It is similar to "Contextual typing of methods and functions in object literals" in  [this PR](https://github.com/microsoft/TypeScript/pull/6739), in that I think that change may have caused at least some of this.

## ✅ Viability Checklist

<!--
   Suggestions that don't meet all these criteria are very, very unlikely to be accepted.
   We always recommend reviewing the TypeScript design goals before investing time writing
   a proposal for ideas outside the scope of the project.
-->
My suggestion meets these guidelines:

* [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
* [x] This wouldn't change the runtime behavior of existing JavaScript code
* [x] This could be implemented without emitting different JS based on the types of the expressions
* [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
* [x] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).


## ⭐ Suggestion

I think your issue template has duplicate "Suggestion" sections...

## 📃 Motivating Example

[Playground link](https://www.typescriptlang.org/play?ssl=43&ssc=3&pln=1&pc=1#code/JYOwLgpgTgZghgYwgAgLITACwPYBMDOyA3gFDLnIzbYBcyAFCHSAK4C2ARtAJTIC8APmQA3bMFwBuEgF8SMFiARhg2EMgDuwLOix589NhhwE6O4-l6kKyQ7oIA6KtnoBGbjJKeA9F+QAVTGBCdWwoAGtCGFAIABpkOAAbXRYAc0xkMABPAAcUbBhkAAMQQpsWfDBkLmR8XIRgKIhcZAgAD2yE4HqwBMySBFUKm0yzPX5iMgoggCFsBVwAYVCoCCVeujAoFljJ8idGZnYuKEtd62QBkHxsBIh7BOwU+iwg+xm5kEXl1Z7M7ilzoCgcDyD5kAA9AD8Z1ksk02iMegMI0RBHcJDBASCyFw2Ag+BAAHIwHEIMIIGpkmlkFoaYRcaAUhlMCg2ohKvg4IZmYz7CR4ZhRgR6FYpvhZvMllAVmtMhstjtrPsmMhWJweBNgZdrrd7o9noF8G9xR8vtKfr1-mcQTbzmCoTCZOiwag4GFGcyUPJFMpVPEMhA2B04JBKMBWviaSSqiwORBA4ROmEULT8DgWAlcETKtUQKt8ZyoJk+WCAIIJa5xTJzVXx5pgbA4xvXSihFrkovxKApdgU6PZbD4fDADi9ZAylhQYfk3ol3ylz5RmxwTKq7A5lBwNTYbK+tTqFlqbXsD3XQyqVkViCEwilgAKAEk58gAGKgRK9OK07ENxvqCBujSlKYCGS4ftg6iEAMQa3JAY4KGEIAQWojSZkacgKEoKj7logqovgfiBsGkAADx+C0rSQJ8hBCvgAgGPhdB+Kc1i2OYjjUK47hwrhtGETBIYQCKZzvJK3yyvK2wxGcyosdY9rQlqgw3HcDxPC8RqiZ8Uoyr8Vq2gZdq+A61jSNJ1hwPguAwJJOzSNwQA)

```typescript
interface Methods {
    foo: (n: number) => void;
}
function withMethods(methods: Methods) {
    methods.foo(1)
}


// This works fine, although type of `n` must be specified explicitly
const myMethods = {
    isBoundCorrectly: true,
    foo(n: number) {
        console.log(this.isBoundCorrectly);
                            // ^?
    }
}
withMethods(myMethods)

// This doesn't, even though it is doing the exact same thing.
withMethods({
    isBoundCorrectly: true,
    foo(n: number) {
        console.log(this.isBoundCorrectly);
                            // ^?
    }
})

// Making the function a template fixes it, but seems like it shouldn't be necessary.
// Also, you need to do so for every argument, possibly recursively.
// And it may not be an option when consuming someone else's API.
// Finally, it is too weak in that it allows completely unknown fields.
function withMethodsTemplate<T extends Methods>(methods: T) {
    methods.foo(1)
}
withMethodsTemplate({
    isBoundCorrectly: true,
    foo(n) {
     // ^?
        console.log(this.isBoundCorrectly);
                            // ^?
    },
    asdf: true,
})
```

## 💻 Use Cases

This seems useful any time you want to pass an object literal with methods to a function.