Repository: TypeScript
Issue: Dynamic object key + discriminated union + typeof could have a better narrowing type
Labels: Suggestion,Awaiting More Feedback

## Search Terms

dynamic object key, discriminated union, typeof, type inference, narrowing type

## Suggestion (or is it a bug report?)

We could write a discriminated union with one of the cases is a dynamic object key. For example...

```ts
type TExample = (
  {
    [key in string]: {
      foo: number
    }
  }
  | {
    errorCode: string
  }
)
```

So, if the key is `errorCode` it could be a `{ foo: number }` or a `string`. If the key is any string that isn't `errorCode`, it should be a `{ foo: number }`.

Then let's check that.

```ts
const func: () => TExample = () => ... // get value from somewhere

const value = func()

if ('errorCode' in value && typeof value.errorCode === 'string') {
  value // what's the type of value here?
}

```

So, makes sense that the `value`'s type should be `{ errorCode: string }`, right?
But the type still is `TExample`! I think that we could have a better narrowing type, because `value` only shoud be `{ errorCode: string }` on this case.

Similarly, would be nice to have that:

```ts
if ('errorCode' in value && typeof value.errorCode === 'object') {
  value // should be { [key in string]: { foo: number } }
}
```

As well as...

```ts
if ('errorCode' in value && typeof value.errorCode === 'number') {
    value // should be never
}
```

[_Full code on playground_](https://www.typescriptlang.org/play/#code/C4TwDgpgBAKgogDwIYFswBtoF4oAoCwAUFFAN5EkkDaA1hCFAJYB2UAzsAE4sDmAugC4yFSqIBmAewlDmAVxQAjCJxEkAviI3EoAH2HaSyzhM4BhCQBMIQjt2Y9NRAJREiAYwnMOUMbOZuhXCcoLAA+WERUDGw8YLC8UigFdCQACyFEyWkoAEYoNXyXQndPbwA3JHRZGN9-INdCRjE8AHIjE3MrFqZWCqroADIBqFBICWa+6oA6drNLbCwcFtteFuDyAyhJ6AB6HfZUiVl0CyToRNnO63YuXnyiLSIm1sv57pYtyuqoIZHwCHGn36M04xjmVhCiygLQkCgAVhA3MA1vpRNsoHsDkcTmcyFBaPQejc7PwMj4pDJ5EpOPl7oRHo1mrg2qCOm8iejfqMARMvhAQWCrpClnJFMoURs0XyMfs2IdjqclFBmBAysoHg0gA)

**Edit**

I just noticed that it happens even when we are not using a dynamic object key...

```ts
type TExample = (
  {
    aaa: {
        foo: number
    }
  }
  | {
    errorCode: string
  }
)

const func: () => TExample = () => ... // get value from somewhere

const value = func()

if ('errorCode' in value && ((typeof value.errorCode) === 'number')) {
    value // type is "{ errorCode: string }" ......  what!? I think that the expected is to be "never"
}
```

[_Playground_](https://www.typescriptlang.org/play/?ssl=1&ssc=1&pln=20&pc=1#code/C4TwDgpgBAKgogDwIYFswBtoF4oAoCwAUFFAN5EklLUBcZFljAZgPYt0B2ArigEYQAnBiQC+DMcSgAfepJKCBLAQGEWAEwh0AzsAEBLDgHNxRAJREiAYxYcdUJlw6W6uU1CwA+WIlQZseN088UihqJDoQ1nYoAEYoEXjzQisbOwA3JHQufwcnVwtCPSY8AHIFJVUNEqgDKAys6AAyRrxcUEgWYvrsgDpylXUIQKwcEu4+QRLTN3I5OszsqAB6Jah26D0tMih+ys0oHX0jeJ7Ts6gAdwALJGAAQgB+IgkiIA)

## Use Cases

I'm opening this issue because I had a problem because of this limitation.

I'm developing a client for an API and, for convention, all errors is returned as `{ errorCode: string }`.
And on an endpoint, the json can be a `{ [key in string]: TComplexObject }` on success case, or be a `{ errorCode: string }` on fail case.
So normally I'm checking if I had an error using `if ('errorCode' in result) {`, but on this endpoint it isn't enough since we have this limitation on TS. Then I don't have a good type inference on this case, needing to write a more complex code.

## Examples

```ts
type TExample = (
  {
    [key in string]: {
      foo: number
    }
  }
  | {
    errorCode: string
  }
)

const func: () => TExample = () => ({ blah: { foo: 1 } })

const value = func()

if ('errorCode' in value && typeof value.errorCode === 'string') {
  value // should be { errorCode: string }
}

if ('errorCode' in value && typeof value.errorCode === 'object') {
  value // should be { [key in string]: { foo: number } }
}

if ('errorCode' in value && typeof value.errorCode === 'number') {
  value // should be never
}
```