Is there a way to create a default value for a typechecked table?

TLDR; I want to be able to set default values for a typechecked table.

For example, This is what I currently use

-- Types
type SettingsType = {
	FPS: number?, -- Default: 20
	TrackNew: boolean?, -- Default: false
	TrackChildren: boolean?, -- Default: true
	TrackNewChildren: boolean?, -- Default: true
	StaticModels: {Instance}?, -- Default: {}
	ActiveModels: {Instance},
}

type SettingsTypeStrict = {
	FPS: number,
	TrackNew: boolean,
	TrackChildren: boolean,
	TrackNewChildren: boolean,
	StaticModels: {Instance},
	ActiveModels: {Instance},
}

local DefaultSettings: SettingsTypeStrict = {
	FPS = 20,
	TrackNew = false,
	TrackChildren = true,
	TrackNewChildren = true,
	StaticModels = {},
	ActiveModels = {}
}

local function NormalizeSettings(Settings: SettingsType): SettingsTypeStrict
	local Current: SettingsTypeStrict = DefaultSettings
	for Property, _ in pairs(DefaultSettings) do
		if Settings[Property] ~= nil then
			Current[Property] = Settings[Property]
		end
	end
	return Current
end

I want to make it so if a SettingsType is declared without any of the optional properties (FPS, TrackNew, etc.) a default value is used (currently stored in DefaultSettings). My solution currently involves two seperate types (SettingsType, SettingsTypeStrict), one containing the optional properties, and one where the optional properties are required. I use a function (NormalizeSettings) to convert from the optional type to the strict type.

Anyone have any experience with this? This is my first type messing around with typechecking so I’d love to be able to get a better understanding of it. I’ve looked through the forum and all the typechecking documentation, however, I haven’t had any luck.

Use a metatable so that if you index something that isnt in the table, itll reference and return what is it is in the default table

Edit: I realise that some people have issues understanding metatables, here’s how you’d do it:

local SettingsWithDefaults:SettingsTypeStrict = setmetatable(Settings,{__index=DefaultSettings})

While still making it a function:

local function NormalizeSettings(Settings: SettingsType): SettingsTypeStrict
	return setmetatable(Settings,{__index=DefaultSettings})
end

happy scripting:)

1 Like

Unfortunately, after trying this, it still does not properly recognize the metatable as the right type.

I’m now getting an error that says

Property 'Settings' is not compatible. Type '{ @metatable { __index: SettingsType }, SettingsType }' could not be converted into 'SettingsType' in an invariant context

Edit: This is what the code looks like right now:

-- Types
type SettingsType = {
	FPS: number?, -- Default: 20
	TrackNew: boolean?, -- Default: false
	TrackChildren: boolean?, -- Default: true
	TrackNewChildren: boolean?, -- Default: true
}

local DefaultSettings: SettingsType = {
	FPS = 20,
	TrackNew = false,
	TrackChildren = true,
	TrackNewChildren = true,
}


export type ReplayType = {
	Frames: {},
	Settings: SettingsType,
	ActiveModels: {Instance},
	StaticModels: {Instance}
}

-- Module
local m = {}

-- Create a new Replay Object
function m.new(s: SettingsType, ActiveModels: {Instance}, StaticModels: {Instance}): ReplayType
	return {
		Frames = {},
		Settings = setmetatable(s, {__index = DefaultSettings}),
		ActiveModels = self.ActiveModels,
		StaticModels = self.StaticModels or {}
	}
end