Skip to content

Instantly share code, notes, and snippets.

@NoriSte
Last active February 15, 2021 15:36
Show Gist options
  • Save NoriSte/32e01b893a59136c21fb30c8944511ef to your computer and use it in GitHub Desktop.
Save NoriSte/32e01b893a59136c21fb30c8944511ef to your computer and use it in GitHub Desktop.
TS Narrow down error, hard-limit to 25 actions?

You can play with the above example in the TypeScript Playground.

This would be a duplicate of this existing issue.


Update: this is another example where it's clear that explicitly creating a valid action works while asking TS to check all the possibible cases fails, probably because of an internal, hardcoded, limit.

/*
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')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment