eniallator

Typescript magic not doing its thing

Sep 10th, 2023 (edited)
1,107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. interface BaseConfig<I extends string, T> {
  2.   id: I;
  3.   default: T;
  4. }
  5.  
  6. interface TextConfig<I extends string> extends BaseConfig<I, string> {
  7.   type: "Text";
  8. }
  9.  
  10. interface NumberConfig<I extends string> extends BaseConfig<I, number> {
  11.   type: "Number";
  12. }
  13.  
  14. interface CheckboxConfig<I extends string> extends BaseConfig<I, boolean> {
  15.   type: "Checkbox";
  16. }
  17.  
  18. type Config<I extends string> =
  19.   | TextConfig<I>
  20.   | NumberConfig<I>
  21.   | CheckboxConfig<I>;
  22.  
  23. type CompleteConfig<C extends Config<string>> = ReadonlyArray<C>;
  24.  
  25. interface StateItem<C extends Config<string>> {
  26.   value: C["default"];
  27.   config: C;
  28. }
  29.  
  30. type State<C extends Config<string>> = {
  31.   [T in C as T["id"]]: StateItem<T>;
  32. };
  33.  
  34. type DeriveStateType<
  35.   I extends string,
  36.   C extends Config<I>
  37. > = C extends BaseConfig<I, infer T> ? T : never;
  38.  
  39. function textConfig<const I extends string>(
  40.   config: Omit<TextConfig<I>, "type">
  41. ): TextConfig<I> {
  42.   return { type: "Text", ...config };
  43. }
  44. function numberConfig<const I extends string>(
  45.   config: Omit<NumberConfig<I>, "type">
  46. ): NumberConfig<I> {
  47.   return { type: "Number", ...config };
  48. }
  49. function checkboxConfig<const I extends string>(
  50.   config: Omit<CheckboxConfig<I>, "type">
  51. ): CheckboxConfig<I> {
  52.   return { type: "Checkbox", ...config };
  53. }
  54.  
  55. function completeConfig<C extends CompleteConfig<Config<string>>>(
  56.   ...configs: C
  57. ): C {
  58.   return configs;
  59. }
  60.  
  61. function state<const C extends Config<string>>(
  62.   configs: CompleteConfig<C>
  63. ): State<C> {
  64.   return configs.reduce(
  65.     (workingState, config) => ({ ...workingState, [config.id]: config }),
  66.     {} as State<C>
  67.   );
  68. }
  69.  
  70. function getStateValue<I extends string, const C extends Config<I>>(
  71.   state: State<C>,
  72.   id: I
  73. ): DeriveStateType<I, C> {
  74.   // Property 'value' does not exist on type 'State<C>[I]'.
  75.   return state[id].value;
  76. }
  77.  
  78. const myConfigs = completeConfig(
  79.   textConfig({ id: "foo", default: "Hello" }),
  80.   numberConfig({ id: "bar", default: 1337 }),
  81.   checkboxConfig({ id: "baz", default: true })
  82. );
  83.  
  84. const myState = state(myConfigs);
  85.  
  86. // I want the below lines to be equivalent but getStateValue is not able to
  87. //   narrow the type down and has type errors.
  88.  
  89. // string
  90. myState["foo"].value;
  91.  
  92. // string | number | boolean
  93. const value = getStateValue(myState, "foo");
  94.  
Advertisement
Add Comment
Please, Sign In to add comment