Literal narrowing lost without explicit const assertion #39763
Copy link
Copy link
Closed
Labels
Working as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
Description
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
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Working as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
Activity
ahejlsberg commentedon Jul 27, 2020
This is working as intended. See #11126, and also #29510.
Aprillion commentedon Jul 28, 2020
I see... Thanks for the explanation provided in the linked PRs - but my intuition was built from IDEs type hint tooltips, neither VS Code nor IntelliJ distinguish between the 2 different types (widening and non-widening literals both look the same):
I wanted to ask where it's documented, but now that I know what to look for, I found it in mentioned in cost assertions section:
The feature was very hard to discover for me (and surprising when I did), but I understand how it makes sense I guess, so I will either get used to it or wait for a new readonly-by-default mode proposed in #32758.
ahejlsberg commentedon Jul 28, 2020
Yeah, we actually discussed displaying something to distinguish between the two, but we decided against the additional noise as in most cases it doesn't matter.