> ## Documentation Index
> Fetch the complete documentation index at: https://flux-docs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Generics

> Type your events so Fire, Connect and Invoke autocomplete correctly.

Flux exports two generic types. Casting your event objects to them gives you argument
checking, return-value checking and autocomplete on both ends.

```luau theme={null}
export type Server<Args... = (...any), Out... = (...any)> = { ... }
export type Client<Args... = (...any), Out... = (...any)> = { ... }
```

<ParamField path="Args..." type="type pack" default="(...any)">
  The arguments carried by the event, which is what you pass to `Fire` and what your
  `Connect` callback receives.
</ParamField>

<ParamField path="Out..." type="type pack" default="(...any)">
  The values returned by an invocation: what `OnInvoke` returns on the server and what
  `Invoke` yields on the client.
</ParamField>

Both default to `(...any)`, which is why untyped Flux code compiles cleanly but gives
you no autocomplete.

## Typing an event

<CodeGroup>
  ```luau Server theme={null}
  local myEvent = Flux.Server("MyEvent") :: Flux.Server<{ Arg: string }>

  myEvent:Fire(player, { Arg = "Hello World" })
  myEvent:FireAll({ Arg = "Broadcast" })
  ```

  ```luau Client theme={null}
  local myEvent = Flux.Client("MyEvent") :: Flux.Client<{ Arg: string }>

  myEvent:Connect(function(data)
  	print(data.Arg) --> "Hello World"
  end)
  ```
</CodeGroup>

Because `Args...` is a type pack, multiple arguments work exactly as you would expect:

```luau theme={null}
local Damage = Flux.Server("Damage") :: Flux.Server<(Instance, number)>

Damage:FireAll(humanoid, 250)
Damage:FireAll(humanoid)       -- analysis error: missing argument
Damage:FireAll(humanoid, "hi") -- analysis error: string ~= number
```

<Note>
  Wrap a multi-value pack in parentheses, as in `<(Instance, number)>`. A single type
  needs no parentheses, as in `<{ Arg: string }>`. Without them, `<Instance, number>` is
  parsed as `Args... = Instance` and `Out... = number`.
</Note>

## Typing an invocation

The second parameter types the return values.

<CodeGroup>
  ```luau Server theme={null}
  local myInvokeEvent = Flux.Server("MyInvokeEvent") :: Flux.Server<(), (boolean, string)>

  myInvokeEvent.OnInvoke = function(player)
  	return true, "Success!"
  end
  ```

  ```luau Client theme={null}
  local myInvokeEvent = Flux.Client("MyInvokeEvent") :: Flux.Client<(), (boolean, string)>

  local success, message = myInvokeEvent:Invoke()
  --    ^ boolean  ^ string
  ```
</CodeGroup>

Use `()` for the empty pack when an event takes no arguments but does return values.

### Arguments and returns together

```luau theme={null}
-- Takes a string, returns (boolean, number).
type PurchaseServer = Flux.Server<(string), (boolean, number)>
type PurchaseClient = Flux.Client<(string), (boolean, number)>

-- Server
local Shop = Flux.Server("Shop") :: PurchaseServer
Shop.OnInvoke = function(player, itemId)
	return true, priceOf(itemId)
end

-- Client
local Shop = Flux.Client("Shop") :: PurchaseClient
local ok, price = Shop:Invoke("sword")
```

## Sharing type definitions

Declaring the pack once in a shared module keeps the two ends from drifting apart. It is
the same problem schemas have, solved the same way.

<CodeGroup>
  ```luau ReplicatedStorage/NetTypes.luau theme={null}
  local Flux = require(game:GetService("ReplicatedStorage").Flux)

  export type MoveArgs = (Instance, Vector3, number)
  export type PurchaseArgs = (string)
  export type PurchaseOut = (boolean, number)

  export type MoveServer = Flux.Server<MoveArgs>
  export type MoveClient = Flux.Client<MoveArgs>
  export type PurchaseServer = Flux.Server<PurchaseArgs, PurchaseOut>
  export type PurchaseClient = Flux.Client<PurchaseArgs, PurchaseOut>

  return {}
  ```

  ```luau Server theme={null}
  local NetTypes = require(ReplicatedStorage.NetTypes)

  local Move = Flux.Server("Move") :: NetTypes.MoveServer
  local Shop = Flux.Server("Shop") :: NetTypes.PurchaseServer
  ```

  ```luau Client theme={null}
  local NetTypes = require(ReplicatedStorage.NetTypes)

  local Move = Flux.Client("Move") :: NetTypes.MoveClient
  local Shop = Flux.Client("Shop") :: NetTypes.PurchaseClient
  ```
</CodeGroup>

## What generics do and do not do

<Warning>
  Generics are a compile-time cast, enforced only by Luau's type checker. They do not
  validate anything at runtime.

  A client that has been tampered with can send any payload it likes, and your typed
  `Connect` callback will happily receive it. Every server listener still has to validate
  its inputs, checking types, clamping ranges and verifying ownership, exactly as you
  would with a raw `RemoteEvent`.
</Warning>

<Info>
  Generics and [schemas](/guides/schemas) are independent and complementary. Generics
  constrain what your Luau code may pass; schemas constrain what goes on the wire.
  Neither implies the other, and a mismatch between them is not reported. A
  `Flux.Server<(string)>` with a `{ T.UInt8() }` schema type-checks fine and fails at
  runtime.
</Info>

## Next steps

<CardGroup cols={2}>
  <Card title="Server type" icon="server" href="/api/server#type-definition">
    The full generic signature of every server method.
  </Card>

  <Card title="Client type" icon="desktop" href="/api/client#type-definition">
    The full generic signature of every client method.
  </Card>
</CardGroup>
