Repository: TypeScript
Issue: Suggestion: assigning index signature to object with optional properties should error if value types mismatch
Labels: Suggestion,In Discussion

# Suggestion

Moving this here from a Twitter discussion with @RyanCavanaugh: https://twitter.com/SeaRyanC/status/1359194486601994241

## 🔍 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:
-->

- index signature
- partial
- optional

## ✅ 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:

* [ ] 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

<!-- A summary of what you'd like to see added or changed -->

Currently this does not error:

```ts
const a: { [key: string]: string } = { foo: 'bar' };
// Expected error here but got none
const t: { foo?: number } = a;
// Runtime type is `string`
// TS type is `number | undefined` ❌
console.log(t.foo);
```

https://www.typescriptlang.org/play?#code/MYewdgzgLgBAhgLhgbxgbQNYFMCeToBOAlmAOYC6+UxZMAvjALwowBmIISA5AEZwFd6AbgBQAejEwAogA8ADlmBQsAExhYCBEARgALDVhg8ArrFIhYYcFhGhIsKElTsQAfiRhjAWx4b6TeFEJGAAlYzAoIi9DKBwFGCIIGAADQhJSZPFJABU4w0SUzx8-AB8YcJUsVhJVZJhAGXJbcAgQABssADpWkFIACigOlwBKUSA

My suggestion is simply that TypeScript should error at the point when we try to assign the index signature type to the object that has an optional property:

```ts
// My suggestion: this should error
const t: { foo?: number } = a;
```

## 📃 Motivating Example

<!--
  If you were announcing this feature in a blog post, what's a short explanation that shows
  a developer why this feature improves the language?
-->

Currently we could run into exceptions at runtime. Example:

```ts
const a: { [key: string]: string } = { foo: 'bar' };
// Expected error here but got none
const t: { foo?: number } = a;
// Runtime exception ❗️
t.foo?.toExponential();
```

https://www.typescriptlang.org/play?#code/MYewdgzgLgBAhgLhgbxgbQNYFMCeToBOAlmAOYC6+UxZMAvjALwowBmIISA5AEZwFd6AbgBQAejEwAogA8ADlmBQsAExhYCBEARgALDVhg8ArrFIhYYcFhGhIsKElTsQAfiRhjAWx4b6TeFEJGAAlYzAoIi9DLBlgLDlI8BhAdXJAeD+RKAA6F1csqBBZOWsIojgAGwAKAEohIA

## 💻 Use Cases

<!--
  What do you want to use this for?
  What shortcomings exist with current approaches?
  What workarounds are you using in the meantime?
-->

This is my real world use case:

I have a type that represents URL query parameters before they have been validated/decoded:
```ts
type QueryParams = {
    [key: string]: string | string[];
};
```

I also have a type that represents query parameters after they have been validated/decoded:

```ts
type QueryData = {
    userId?: number;
};
```

I have some functions which expect `QueryData`, but it's very easy to make the mistake of passing in `QueryParams` instead.

```ts
declare const redirectToUserPage: (queryData: QueryData) => void;

declare const queryParams: QueryParams;
declare const queryData: QueryData;

// This is a mistake but TS produces no error
redirectToUserPage(queryParams);
// We should have passed in `queryData` instead
```