|
|
|
/* |
|
Narrowing down type issue: the 26th "success action" breaks TS at |
|
`const action: Action<AcceptedType> = { type, success: true }` |
|
with the following error: |
|
|
|
``` |
|
Type |
|
'{ type: "Action_1" | "Action_2" | "Action_3" | "Action_4" | "Action_5" | "Action_6" | "Action_7" | "Action_8" | "Action_9" | "Action_10" | "Action_11" | "Action_12" | "Action_13" | ... 12 more ... | "Action_26"; success: true; }' |
|
is not assignable to type 'ActionSuccess'. |
|
|
|
Type |
|
'{ type: "Action_1" | "Action_2" | "Action_3" | "Action_4" | "Action_5" | "Action_6" | "Action_7" | "Action_8" | "Action_9" | "Action_10" | "Action_11" | "Action_12" | "Action_13" | ... 12 more ... | "Action_26"; success: true; }' |
|
is not assignable to type 'Action_26_Success'. |
|
Types of property 'type' are incompatible. |
|
|
|
Type |
|
'"Action_1" | "Action_2" | "Action_3" | "Action_4" | "Action_5" | "Action_6" | "Action_7" | "Action_8" | "Action_9" | "Action_10" | "Action_11" | "Action_12" | "Action_13" | "Action_14" | ... 11 more ... | "Action_26"' |
|
is not assignable to type '"Action_26"'. |
|
Type '"Action_1"' is not assignable to type '"Action_26"'. |
|
|
|
``` |
|
|
|
by removing the `success: true` from the 26th action, or from another random action, the error disappears. |
|
*/ |
|
|
|
|
|
interface Action_0_Failure { type: 'Action_0', failure: true } |
|
|
|
interface Action_1_Success { type: 'Action_1', success: true } |
|
interface Action_2_Success { type: 'Action_2', success: true } |
|
interface Action_3_Success { type: 'Action_3', success: true } |
|
interface Action_4_Success { type: 'Action_4', success: true } |
|
interface Action_5_Success { type: 'Action_5', success: true } |
|
interface Action_6_Success { type: 'Action_6', success: true } |
|
interface Action_7_Success { type: 'Action_7', success: true } |
|
interface Action_8_Success { type: 'Action_8', success: true } |
|
interface Action_9_Success { type: 'Action_9', success: true } |
|
interface Action_10_Success { type: 'Action_10', success: true } |
|
interface Action_11_Success { type: 'Action_11', success: true } |
|
interface Action_12_Success { type: 'Action_12', success: true } |
|
interface Action_13_Success { type: 'Action_13', success: true } |
|
interface Action_14_Success { type: 'Action_14', success: true } |
|
interface Action_15_Success { type: 'Action_15', success: true } |
|
interface Action_16_Success { type: 'Action_16', success: true } |
|
interface Action_17_Success { type: 'Action_17', success: true } |
|
interface Action_18_Success { type: 'Action_18', success: true } |
|
interface Action_19_Success { type: 'Action_19', success: true } |
|
interface Action_20_Success { type: 'Action_20', success: true } |
|
interface Action_21_Success { type: 'Action_21', success: true } |
|
interface Action_22_Success { type: 'Action_22', success: true } |
|
interface Action_23_Success { type: 'Action_23', success: true } |
|
interface Action_24_Success { type: 'Action_24', success: true } |
|
interface Action_25_Success { type: 'Action_25', success: true } |
|
interface Action_26_Success { type: 'Action_26', success: true } // the 26th action with `success: true` causes the problem. By removing `success: true` the error disappears |
|
|
|
type ActionSuccess = Action_1_Success | Action_2_Success | Action_3_Success | Action_4_Success | Action_5_Success | Action_6_Success | Action_7_Success | Action_8_Success | Action_9_Success | Action_10_Success | Action_11_Success | Action_12_Success | Action_13_Success | Action_14_Success | Action_15_Success | Action_16_Success | Action_17_Success | Action_18_Success | Action_19_Success | Action_20_Success | Action_21_Success | Action_22_Success | Action_23_Success | Action_24_Success | Action_25_Success | Action_26_Success |
|
|
|
|
|
// see https://stackoverflow.com/a/50499316/3508387 |
|
type NarrowAction<T, K> = T extends { type: K } ? T : never |
|
|
|
type Action<K extends ActionSuccess['type']> = NarrowAction<ActionSuccess, K> |
|
|
|
type GenericSuccess<T> = T extends { success: true } ? T : never |
|
|
|
type AcceptedType = GenericSuccess<ActionSuccess>['type'] |
|
|
|
const callback = (type: AcceptedType) => { |
|
const action: Action<AcceptedType> = { type, success: true } // here's the TS error |
|
return action |
|
} |
|
|
|
// correct usage |
|
callback('Action_1') |
|
callback('Action_25') |
|
|
|
// wrong usage, protected by TS |
|
// @ts-expect-error |
|
callback('Action_0') |
|
// @ts-expect-error |
|
callback('unexisting action') |