Why does the following code not typecheck (TypeScript 5.5.4):
type ValidationResult$Type = 'success' | 'invalid_or_expired_token'
export type ValidationResult = {
type: 'success'
token_info: object
} | {
type: 'invalid_or_expired_token'
};
const x: ValidationResult = undefined as unknown as ValidationResult;
const {type}: {type: ValidationResult$Type} = x;
switch (type) {
case 'success':
const {token_info} = x;
break;
case 'invalid_or_expired_token':
break;
}
while the below, simpler, code does:
// type ValidationResult$Type = 'success' | 'invalid_or_expired_token'
export type ValidationResult = {
type: 'success'
token_info: object
} | {
type: 'invalid_or_expired_token'
};
const x: ValidationResult = undefined as unknown as ValidationResult;
// const {type}: {type: ValidationResult$Type} = x;
const {type} = x;
switch (type) {
case 'success':
const {token_info} = x;
break;
case 'invalid_or_expired_token':
break;
}
In this demo you can see the two cases together:
function f1() {
const {type} = x;
switch (type) { // type is "success" | "invalid_or_expired_token"
case 'success':
const test1 = x.token_info; // OK
break;
case 'invalid_or_expired_token':
const test2 = x.token_info; // Error
break;
}
}
In this case, type
is "success" | "invalid_or_expired_token"
. When TypeScript is processing the control flow analysis, it is able to see that type
comes from x.type
, then TS is smart enough to guess that if x.type
is "success"
, then x
has the property token_info
.
function f2() {
type ValidationResult$Type = ValidationResult['type'];
const {type}: {type: ValidationResult$Type} = x;
switch (type) { // type is "success" | "invalid_or_expired_token"
case 'success':
const test1 = x.token_info; // Error
break;
case 'invalid_or_expired_token':
const test2 = x.token_info; // Error
break;
}
}
In this case, type
is also"success" | "invalid_or_expired_token"
. When TypeScript is processing the control flow analysis, it is NOT able to see that type
comes from x.type
. Your annotation {type: ValidationResult$Type}
takes precedence over inferring it from x
, then TS has no way to connect the dots and guess that if type
is "success"
, then x
has the property token_info
.
The moral of the story is that you should avoid adding manual type annotation whenever possible.