Repository: TypeScript
Issue: Using `undefined` as a discriminator within a mapped type yields erroneous types when primitive intersections are involved
Labels: Needs Investigation

**TypeScript Version:**  2.2.0

[**Code**](http://www.typescriptlang.org/play/#src=%2F%2F%20A%20*self-contained*%20demonstration%20of%20the%20problem%20follows...%0D%0Atype%20DeepReadonly%3CT%3E%20%3D%20%7B%0D%0A%20%20%20%20readonly%20%5BK%20in%20keyof%20T%5D%3A%20DeepReadonly%3CT%5BK%5D%3E%3B%0D%0A%7D%0D%0A%0D%0Ainterface%20FieldBrand%20%7B%0D%0A%20%20%20%20%22%20do%20not%20use%20%22%3A%20void%3B%0D%0A%7D%0D%0A%0D%0Atype%20FieldId%20%3D%20number%20%26%20FieldBrand%3B%0D%0A%0D%0Ainterface%20DefOne%20%7B%0D%0A%20%20%20%20field%3A%20string%20%7C%20FieldId%3B%0D%0A%20%20%20%20kind%3A%20string%3B%0D%0A%7D%0D%0A%0D%0Ainterface%20DefTwo%20%7B%0D%0A%20%20%20%20field%3F%3A%20undefined%3B%20%2F%2F%20Allow%20discriminant%20checks%20on%20'field'%0D%0A%20%20%20%20value%3A%20string%3B%0D%0A%7D%0D%0A%0D%0Atype%20Def%20%3D%20DefOne%20%7C%20DefTwo%3B%0D%0A%0D%0Ainterface%20State%20%7B%0D%0A%20%20%20%20a%3F%3A%20Def%3B%0D%0A%20%20%20%20b%3F%3A%20Def%3B%0D%0A%7D%0D%0A%0D%0Atype%20ROState%20%3D%20DeepReadonly%3CState%3E%3B%0D%0A%0D%0Afunction%20lookupName(f%3A%20FieldId)%3A%20string%20%7B%0D%0A%20%20%20%20return%20%22%22%3B%0D%0A%7D%0D%0A%0D%0Afunction%20remapFieldsToNames(channels%3A%20ROState)%3A%20ROState%20%7B%0D%0A%20%20%20%20const%20newState%3A%20State%20%3D%20%7B%7D%3B%0D%0A%20%20%20%20for%20(const%20k%20of%20Object.keys(channels))%20%7B%0D%0A%20%20%20%20%20%20%20%20const%20key%20%3D%20k%20as%20keyof%20ROState%3B%0D%0A%20%20%20%20%20%20%20%20const%20ch%20%3D%20channels%5Bkey%5D%3B%0D%0A%20%20%20%20%20%20%20%20let%20replacement%3A%20ROState%5Btypeof%20key%5D%20%7C%20undefined%20%3D%20undefined%3B%0D%0A%20%20%20%20%20%20%20%20if%20(ch)%20%7B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(ch.field)%20%7B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20f%20%3D%20ch.field%3B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(typeof%20f%20%3D%3D%3D%20%22number%22)%20%7B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%3B%20%2F%2F%20Should%20be%20FieldId%20or%20number%2C%20not%20never!%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20replacement%20%3D%20%7B%20...ch%2C%20field%3A%20lookupName(f)%20%7D%3B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20if%20(typeof%20f%20%3D%3D%3D%20%22string%22)%20%7B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%3B%20%2F%2F%20correct%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%20%20%20%20newState%5Bk%5D%20%3D%20replacement%20%7C%7C%20channels%5Bk%5D%3B%0D%0A%20%20%20%20%7D%0D%0A%20%20%20%20return%20newState%3B%0D%0A%7D%0D%0A)

```ts
// A *self-contained* demonstration of the problem follows...
type DeepReadonly<T> = {
    readonly [K in keyof T]: DeepReadonly<T[K]>;
}

interface FieldBrand {
    " do not use ": void;
}

type FieldId = number & FieldBrand;

interface DefOne {
    field: string | FieldId;
    kind: string;
}

interface DefTwo {
    field?: undefined; // Allow discriminant checks on 'field'
    value: string;
}

type Def = DefOne | DefTwo;

interface State {
    a?: Def;
    b?: Def;
}

type ROState = DeepReadonly<State>;

function lookupName(f: FieldId): string {
    return "";
}

function remapFieldsToNames(channels: ROState): ROState {
    const newState: State = {};
    for (const k of Object.keys(channels)) {
        const key = k as keyof ROState;
        const ch = channels[key];
        let replacement: ROState[typeof key] | undefined = undefined;
        if (ch) {
            if (ch.field) {
                const f = ch.field;
                if (typeof f === "number") {
                    f; // Should be FieldId or number, not never!
                    replacement = { ...ch, field: lookupName(f) };
                }
                else if (typeof f === "string") {
                    f; // correct
                }
            }
        }
        newState[k] = replacement || channels[k];
    }
    return newState;
}
```

**Expected behavior:**
There are no type errors in the above, and `f` after the `typeof f === "number"` check is either a number or `FieldId`.

**Actual behavior:**
`replacement = { ...ch, field: lookupName(f) };` has a type error and `f` is of type `never`.