How to type a table where certain fields are mutually exclusive?

Hi everyone,

I’m working with the type system and trying to define a type for a table where only one group of related fields is allowed at a time, and all others are disallowed.

The general idea

I want to define a type Props = ... that enforces rules like:

  • If field A exists, then B, C, D, etc. must not exist.
  • If field B exists, then A, C, D, etc. must not exist.
  • Some fields like C and D may be allowed together, but not with A or B.

I’m looking for the cleanest or most idiomatic way to express this kind of mutual exclusivity


Example use case

Here’s a specific case I’m working on. I have a Props table where users can define padding values in one of several mutually exclusive ways:

  • Padding — applies to all sides
  • Vertical — applies to top and bottom
  • Horizontal — applies to left and right
  • Top, Bottom, Left, Right — can be used individually or together

Each field is of type number. The exclusivity rules are:

  • If Padding exists, no other padding-related fields are allowed
  • If Vertical exists, then Padding, Top, and Bottom are disallowed
  • If Horizontal exists, then Padding, Left, and Right are disallowed
  • If Left or Right exists, then Padding and Horizontal are disallowed
  • If Top or Bottom exists, then Padding and Vertical are disallowed

I’d like to define a Props type that enforces these constraints at the type level.

Has anyone solved something similar, or know the best approach to modeling mutually exclusive field sets like this?

1 Like

Just don’t type those fields. The types do not do anything except give you editor suggestions.

1 Like

Your response is ‘just don’t do it’, and isn’t helpful.

2 Likes

i don’t think it’s possible to do even with the new typechecker, so i’d say the same; “just don’t do it”

1 Like

Very much possible with type functions in the new solver

1 Like

If you are trying to solve a problem that nets you no observable result to players, why don’t you go play a puzzle game?

1 Like

This is possible with type unions. You can define each combination and it will work as expected:

--!strict

type A = {
	A: string,
	[string]: nil,
}
type B = {
	B: string,
	[string]: nil,
}
type CD = {
	C: string?,
	D: string?,
	[string]: nil,
}
type all = A | B | CD

local test1: all = {
	A = "Test",
} --> valid
local test2: all = {
	B = "Test",
} --> valid
local test3: all = {
	C = "Test",
} --> valid
local test4: all = {
	D = "Test",
} --> valid
local test5: all = {
	C = "Test",
	D = "Test",
} --> valid
local test6: all = {
	C = "Test",
	D = "Test",
	A = "Test",
} --> warns
local test6: all = {
	A = "Test",
	B = "Test",
} --> warns

Now you may be confused about [string]: nil. This is required if you want the exact format of your type. In Luau, this is valid:

type example = {x: number}
local tab: example = {x = 1, y = 1} --> valid, even though "y" is not defined in the type

So we have to explicity say that anything else cannot exist with [string]: nil:

type example = {x: number, [string]: nil}
local tab: example = {x = 1, y = 1} --> not valid, because "x" is a number and everything else should not exist

Although note that this currently only works when constructing the table, and doesn’t currently provide meaningful intellisense.


@azqjanna There are different reasons people want typechecking. Just because there’s “no observable result to players” does not invalidate the motive. Typechecking is there to provide developers with smarter intellisense and performance boosts in the future.

2 Likes

Was aware of this possibility for the type, in terms of the [string]: nil syntax, but was exploring the options for intellisense, your response has given me enough confidence that the exact thing I’m after isn’t possible in the old solver

Thanks for the meaningful response

2 Likes

To anyone who was looking for an actual solution, the best youll have before type functions arrive will be making all fields optional, and narrowing the type down within your function. You miss out on some valuable intellisense which points users in the right direction, but for the time being it’s the best available.