#Module/plugin loader - how to create a factory function (ChatGPT directed me here)

6 messages · Page 1 of 1 (latest)

timber dawn
#
type Mod1State = { config: {timeout: number} }
type Mod2State = { config: {defaultSpeed: number}, speed: number }

const Mod1 = {
  init(config: {timeout: number}): Mod1State {
    return {
      config: {
        timeout: config.timeout,
      }
    }
  }
}

const Mod2 = {
  init(config: {speed: number}): Mod2State {
    return {
      config: {
        defaultSpeed: config.speed,
      },
      speed: 0,
    }
  },

  api(state: Mod2State) {
    return {
      speed() {
        return state.speed
      },
    }
  }
}

const RegisteredModules = {
  Mod1,
  Mod2,
}

function initModule<N extends keyof typeof RegisteredModules>(name: N, config: Parameters<typeof RegisteredModules[N]['init']>[0]) {
  const mod = RegisteredModules[name]
  const state = mod.init(config)
  return {
    mod,
    config,
    state,
  }
}

export { initModule }

Gives this error inside the initModule() function, the mod.init(config) line for the config property:

Argument of type '{ timeout: number; } | { speed: number; }' is not assignable to parameter of type '{ timeout: number; } & { speed: number; }'.
  Type '{ timeout: number; }' is not assignable to type '{ timeout: number; } & { speed: number; }'.
    Property 'speed' is missing in type '{ timeout: number; }' but required in type '{ speed: number; }'. 

Why doesn't the generic in initModule() work?

The actual code has >30 modules like Mod1 & Mod2, with slightly different sets of functions and different init parameters.
I've asked ChatGPT, but it didn't know a solution and eventually suggested to go to Typescript discord 🙂

fossil lanternBOT
#
juodumas#7250

Preview:```ts
type Mod1State = {config: {timeout: number}}
type Mod2State = {
config: {defaultSpeed: number}
speed: number
}

const Mod1 = {
init(config: {timeout: number}): Mod1State {
return {
config: {
timeout: config.timeout,
},
}
},
...```

finite loom
#

!:corr*

fossil lanternBOT
#
Retsam19#2505
`!retsam19:correspondence-problem`:

There's a particular pattern that is safe but hard for the Typescript compiler to handle, which I call the "correspondence problem":

const functionsWithArguments = [
  { func: (arg: string) => {}, arg: "foo" },
  { func: (arg: number) => {}, arg: 0 },
];

for (const { func, arg } of functionsWithArguments) {
  func(arg);
//     ^^^
// Argument of type 'string | number' is not assignable to parameter of type 'never'.
//   Type 'string' is not assignable to type 'never'.
}

The problem is that func is typed as (x: string) => void | (x: number) => void and arg is string | number, but the compiler can't prove that they "correspond": that, for example, arg is only a string when func accepts strings.

As far as the type are concerned, arg could be number, and func could be (arg: string) => void, and that would be a type-error. It's easy for us to see that that won't happen, but that requires understanding the program at a higher-level than the level the compiler operates.

Depending on the specifics there's sometimes clever fixes, but usually I recommend using a type assertion and ignoring the issue:

func(arg as never);
finite loom
#

@timber dawn ^- tl;dr typescript isn't smart enough to know that the given config matches the parameter of the init function

#

is there a reason you can't expose the modules directly and get users to do module.init?