# TypeScript Module Error: JavaScript functions cannot be inline

**URL:** <https://forum.heroiclabs.com/t/typescript-module-error-javascript-functions-cannot-be-inline/3305>\
**Category:** Runtime Framework\
**Tags:** server-framework, typescript\
**Created:** [November 30, 2022, 1:51am UTC](https://forum.heroiclabs.com/t/typescript-module-error-javascript-functions-cannot-be-inline/3305 "2022-11-30T01:51:34Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![hcopp](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.heroiclabs.com/hcopp/32/1455_2.png) [@hcopp](https://forum.heroiclabs.com/u/hcopp)\
**Post date:** [November 30, 2022, 1:51am UTC](https://forum.heroiclabs.com/t/typescript-module-error-javascript-functions-cannot-be-inline/3305/1 "2022-11-30T01:51:34Z")

</div>

I am writing my server side code for my game in TypeScript, and built a base Game class with extending mini-games. Inside of each Game or mini game class is all the functions needed for registerMatch. When I attempt to run my code, I get this error:

```auto
{"level":"fatal","ts":"2022-11-30T01:31:18.121Z","caller":"main.go:158","msg":"Failed initializing runtime modules","error":"GoError: js match handler \"matchInit\" function for module \"ballgame\" global id could not be extracted: function literal found: javascript functions cannot be inlined\n\tat github.com/heroiclabs/nakama/v3/server.(*RuntimeJavascriptInitModule).registerMatch.func1 (native)\n\tat InitModule (index.js:407:26(39))\n\tat native\n"}

```

I am running Nakama 3.14 and am using TypeScript/JavaScript.

My code is setup as such:  
Main.ts

```auto
import { Game } from './games'
import { BallGame } from './games/ball'

const InitModule = ... {
    const game: Game = new BallGame()
    initializer.registerMatch(game.id, {
        matchInit: game.matchInit,
        matchJoinAttempt: game.matchJoinAttempt,
        matchJoin: game.matchJoin,
        matchLeave: game.matchLeave,
        matchLoop: game.matchLoop,
        matchSignal: game.matchSignal,
        matchTerminate: game.matchTerminate,
    })
}

```

Games.ts

```auto
export abstract class Game {
    id: string
    ...

    matchInit(
        ctx: nkruntime.Context,
        logger: nkruntime.Logger,
        nk: nkruntime.Nakama,
        params: { [key: string]: string }
    ): { state: GameState; tickRate: number; label: string } {
        ...
        return {
            state: { ... },
            tickRate: this.tickRate,
            label: gameInviteCode,
        }
    }

    // ... (all other functions needed for registerMatch)
}

```

BallGame.ts

```auto
export class BallGame extends Game {
    id = 'ball-game'
    ...
    matchInit(
        ctx: nkruntime.Context,
        logger: nkruntime.Logger,
        nk: nkruntime.Nakama,
        params: { [key: string]: string }
    ): { state: BallGameState; tickRate: number; label: string } {
        const { state, tickRate, label } = super.matchInit(ctx, logger, nk, params)
        ...
        return {
            state: {
                ...state,
                ...
            },
            tickRate,
            label,
        }
    }
}

```

I see that this error is referencing me using functions that then get compiled to be inline, but is there anything I can do here to keep my functionality the same (ie using classes of some sort and hopefully calling super)? I tried converting to arrow functions without success (may have been doing it wrong).

Thanks for any help you may have.

---

<div class="post-metadata">

**Author:** ![sesposito](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.heroiclabs.com/sesposito/32/2594_2.png) [@sesposito](https://forum.heroiclabs.com/u/sesposito)\
**Post date:** [November 30, 2022, 11:14am UTC](https://forum.heroiclabs.com/t/typescript-module-error-javascript-functions-cannot-be-inline/3305/2 "2022-11-30T11:14:09Z")

</div>

Hello @hcopp, unfortunately there are some limitations on the JS engine and all the functions that are registered need to be at the top level scope. I believe that classes can be referenced within those functions, but not the other way around. Using classes anywhere else should work too.

You need to make sure that in the transpiled code the functions are not inlined but referenced by a top level identifier within the registration functions.

Hope this helps.

---

<div class="post-metadata">

**Author:** ![StudioParaNoya](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.heroiclabs.com/studioparanoya/32/1854_2.png) [@StudioParaNoya](https://forum.heroiclabs.com/u/StudioParaNoya)\
**Post date:** [January 15, 2024, 1:52am UTC](https://forum.heroiclabs.com/t/typescript-module-error-javascript-functions-cannot-be-inline/3305/3 "2024-01-15T01:52:24Z")

</div>

Hi @sesposito,

I am commenting here to not open a new ticket for the same issue.

I have the same error about inlined functions but I do believe that my functions are already top-level.

I did the same logic where I have a match\_handler class that implements from nkruntime.MatchHandler. Then, I have multiple sub match\_handler classes that implements from match\_handler, such as five\_hand\_poker\_match\_handler.

Here is a snippet from each classes:

```auto
interface MatchLabel {
  //CasiNoya Labels
  training_five_hand_poker: number;
  training_blackjack: number;
  training_chinese_poker: number;
  training_war: number;

  normal_five_hand_poker: number;
  normal_blackjack: number;
  normal_chinese_poker: number;
  normal_war: number;

  rank_five_hand_poker: number;
  rank_blackjack: number;
  rank_chinese_poker: number;
  rank_war: number;
}

interface State {
  // The winner of the current game.
  // The winner positions.

  // FOR CASINOYA
  // Match label
  label: MatchLabel;
  // Ticks where no actions have occurred.
  emptyTicks: number;
  // Currently connected users, or reserved spaces.
  presences: { [userId: string]: nkruntime.Presence | null };
  // Number of users currently in the process of connecting to the match.
  joinsInProgress: number;
  // True if there's a game currently in progress.
  playing: boolean;
  // Current state of the game.
  game_state: GameState;
  // Ticks until they must submit their move.
  deadlineRemainingTicks: number;
  // Ticks until the next game starts, if applicable.
  nextGameRemainingTicks: number;
  // AI playing mode
  ai: boolean;
  // A move message from AI player
  aiMessage: nkruntime.MatchMessage | null;
}

abstract class Match_Handler implements nkruntime.MatchHandler<State> {
  protected moduleName: string;
  protected tickRate: number;
  protected maxEmptySec: number;
  protected delaybetweenGamesSec: number;
  protected turnTimeFastSec: number;
  protected turnTimeNormalSec: number;

  constructor(
    $moduleName: string,
    $tickRate: number,
    $maxEmptySec: number,
    $delaybetweenGamesSec: number,
    $turnTimeFastSec: number,
    $turnTimeNormalSec: number
  ) {
    this.moduleName = $moduleName;
    this.tickRate = $tickRate;
    this.maxEmptySec = $maxEmptySec;
    this.delaybetweenGamesSec = $delaybetweenGamesSec;
    this.turnTimeFastSec = $turnTimeFastSec;
    this.turnTimeNormalSec = $turnTimeNormalSec;
  }

  abstract matchInit(
    ctx: nkruntime.Context,
    logger: nkruntime.Logger,
    nk: nkruntime.Nakama,
    params: { [key: string]: string }
  ): { state: State; tickRate: number; label: string };

```

```auto
class Five_Hand_Poker_Match_Handler extends Match_Handler {
  private static _instance_normal?: Five_Hand_Poker_Match_Handler;
  private static _instance_training?: Five_Hand_Poker_Match_Handler;

  private constructor(
    $moduleName: string,
    $tickRate: number,
    $maxEmptySec: number,
    $delaybetweenGamesSec: number,
    $turnTimeFastSec: number,
    $turnTimeNormalSec: number
  ) {
    super(
      $moduleName,
      $tickRate,
      $maxEmptySec,
      $delaybetweenGamesSec,
      $turnTimeFastSec,
      $turnTimeNormalSec
    );
  }

  matchInit(
    ctx: nkruntime.Context,
    logger: nkruntime.Logger,
    nk: nkruntime.Nakama,
    params: { [key: string]: string }
  ) {
    let label: MatchLabel;
    if (this.$moduleName == "five_hand_poker_training") {
      label = {
        //CasiNoya Labels
        training_five_hand_poker: 1,
        training_blackjack: 0,
        training_chinese_poker: 0,
        training_war: 0,

        normal_five_hand_poker: 0,
        normal_blackjack: 0,
        normal_chinese_poker: 0,
        normal_war: 0,

        rank_five_hand_poker: 0,
        rank_blackjack: 0,
        rank_chinese_poker: 0,
        rank_war: 0,
      };
    } else {
      label = {
        //CasiNoya Labels
        training_five_hand_poker: 0,
        training_blackjack: 0,
        training_chinese_poker: 0,
        training_war: 0,

        normal_five_hand_poker: 1,
        normal_blackjack: 0,
        normal_chinese_poker: 0,
        normal_war: 0,

        rank_five_hand_poker: 0,
        rank_blackjack: 0,
        rank_chinese_poker: 0,
        rank_war: 0,
      };
    }

    let state: State = {
      // The winner of the current game.
      // The winner positions.

      // FOR CASINOYA
      // Match label
      label: label,
      // Ticks where no actions have occurred.
      emptyTicks: 0,
      // Currently connected users, or reserved spaces.
      presences: {},
      // Number of users currently in the process of connecting to the match.
      joinsInProgress: 0,
      // True if there's a game currently in progress.
      playing: false,
      // Current state of the game.
      game_state: GameState.LOBBY,
      // Ticks until they must submit their move.
      deadlineRemainingTicks: 0,
      // Ticks until the next game starts, if applicable.
      nextGameRemainingTicks: 0,
      // AI playing mode
      ai: false,
      // A move message from AI player
      aiMessage: null,
    };
    return {
      state: state,
      tickRate: this.tickRate, // 1 tick per second = 1 MatchLoop func invocations per second
      label: JSON.stringify(label),
    };
  }

```

Am I wrong to assume that I already have top-level functions? If yes, can you provide some indications on how to perform the desired tasks of having multiple modules for multiple different game modes?  
Thanks!

---

<div class="post-metadata">

**Author:** ![sesposito](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.heroiclabs.com/sesposito/32/2594_2.png) [@sesposito](https://forum.heroiclabs.com/u/sesposito)\
**Post date:** [January 15, 2024, 10:01am UTC](https://forum.heroiclabs.com/t/typescript-module-error-javascript-functions-cannot-be-inline/3305/4 "2024-01-15T10:01:09Z")

</div>

I’m not entirely sure how class types get transpiled to ES5 compliant JS code, but if the `matchInit` function is declared inside a class I don’t think it’ll be globally scoped at the top level.

---

<div class="post-metadata">

**Author:** ![StudioParaNoya](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.heroiclabs.com/studioparanoya/32/1854_2.png) [@StudioParaNoya](https://forum.heroiclabs.com/u/StudioParaNoya)\
**Post date:** [January 16, 2024, 5:46am UTC](https://forum.heroiclabs.com/t/typescript-module-error-javascript-functions-cannot-be-inline/3305/5 "2024-01-16T05:46:52Z")

</div>

Thank you, I got it to work. I figured out a way to declare the functions in variables then have different versions of the functions depending on the game mode!
