Skip to content

Literal narrowing lost without explicit const assertion #39763

Closed
@Aprillion

Description

@Aprillion

TypeScript Version: 3.9.2

Search Terms:
const assertion, literal narrowing

Code

const a1 = 'A1'
const a2 = 'A2'

const actionCreators = {
    a1: () => ({type: a1}),
    a2: (b: number) => ({type: a2, b}),
}
type Action = ReturnType<typeof actionCreators[keyof typeof actionCreators]>

export default (state = {}, action: Action) => {
    switch (action.type) {
        case a2: 
            return {...state, b: action.b}
        default:
            return state
    }
}

Expected behavior:
Code with literal narrowing should compile, similar to the following code with an explicit const assertion:

const a1 = 'A1' as const
const a2 = 'A2' as const
...

Actual behavior:
Code does not compile.

Property 'b' does not exist on type '{ type: string; } | { type: string; b: number; }'.
  Property 'b' does not exist on type '{ type: string; }'.(2339)

Related Issues:
Might be related to #9998.

Output
const a1 = 'A1'; // as const
const a2 = 'A2'; // as const
const actionCreators = {
    a1: () => ({ type: a1 }),
    a2: (b) => ({ type: a2, b }),
};
export default (state = {}, action) => {
    switch (action.type) {
        case a2:
            return Object.assign(Object.assign({}, state), { b: action.b });
        default:
            return state;
    }
};
Compiler Options
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "declaration": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "moduleResolution": 2,
    "target": "ES2017",
    "jsx": "React",
    "module": "ESNext"
  }
}

Playground Link: Provided

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions