diff --git a/src/rogue-properties/src/Shared/Array/RoguePropertyArrayUtils.lua b/src/rogue-properties/src/Shared/Array/RoguePropertyArrayUtils.lua index ae45907071..c66a8b6741 100644 --- a/src/rogue-properties/src/Shared/Array/RoguePropertyArrayUtils.lua +++ b/src/rogue-properties/src/Shared/Array/RoguePropertyArrayUtils.lua @@ -1,4 +1,4 @@ ---!nonstrict +--!strict --[=[ @class RoguePropertyArrayUtils ]=] @@ -7,6 +7,7 @@ local require = require(script.Parent.loader).load(script) local DefaultValueUtils = require("DefaultValueUtils") local RoguePropertyArrayConstants = require("RoguePropertyArrayConstants") +local RoguePropertyTypes = require("RoguePropertyTypes") local String = require("String") local RoguePropertyArrayUtils = {} @@ -19,8 +20,14 @@ function RoguePropertyArrayUtils.getIndexFromName(name: string): number? return tonumber(String.removePrefix(name, RoguePropertyArrayConstants.ARRAY_ENTRY_PERFIX)) end -function RoguePropertyArrayUtils.createRequiredPropertyDefinitionFromArray(arrayData, parentPropertyTableDefinition) - local expectedType = typeof(arrayData[1]) +function RoguePropertyArrayUtils.createRequiredPropertyDefinitionFromArray( + arrayData: { any }, + parentPropertyTableDefinition: RoguePropertyTypes.RoguePropertyDefinition +): ( + RoguePropertyTypes.RoguePropertyDefinition?, + string? +) + local expectedType: string? = typeof(arrayData[1]) if expectedType == "table" then return RoguePropertyArrayUtils.createRequiredTableDefinition(arrayData, parentPropertyTableDefinition) elseif expectedType == "nil" then @@ -31,14 +38,22 @@ function RoguePropertyArrayUtils.createRequiredPropertyDefinitionFromArray(array if typeof(item) ~= expectedType then expectedType = nil -- TODO: Maybe union? - return nil, string.format("Expected type %q on %q, got %q", expectedType, tostring(index), typeof(item)) + return nil, + string.format("Expected type %q on %q, got %q", expectedType :: any, tostring(index), typeof(item)) end end - return RoguePropertyArrayUtils.createRequiredPropertyDefinitionFromType(expectedType, parentPropertyTableDefinition) + -- expectedType is only ever nil on a path that returns above, so it is a string here. + return RoguePropertyArrayUtils.createRequiredPropertyDefinitionFromType( + expectedType :: string, + parentPropertyTableDefinition + ) end -function RoguePropertyArrayUtils.createRequiredTableDefinition(arrayData, parentPropertyTableDefinition) +function RoguePropertyArrayUtils.createRequiredTableDefinition( + arrayData: { any }, + parentPropertyTableDefinition: RoguePropertyTypes.RoguePropertyDefinition +): (RoguePropertyTypes.RoguePropertyDefinition?, string?) local RoguePropertyTableDefinition = (require :: any)("RoguePropertyTableDefinition") local entry = arrayData[1] @@ -64,7 +79,10 @@ end function RoguePropertyArrayUtils.createRequiredPropertyDefinitionFromType( expectedType: string, - parentPropertyTableDefinition + parentPropertyTableDefinition: RoguePropertyTypes.RoguePropertyDefinition +): ( + RoguePropertyTypes.RoguePropertyDefinition?, + string? ) local RoguePropertyDefinition = (require :: any)("RoguePropertyDefinition") @@ -81,7 +99,10 @@ function RoguePropertyArrayUtils.createRequiredPropertyDefinitionFromType( return propertyDefinition end -function RoguePropertyArrayUtils.createDefinitionsFromContainer(container: Instance, parentPropertyTableDefinition) +function RoguePropertyArrayUtils.createDefinitionsFromContainer( + container: Instance, + parentPropertyTableDefinition: RoguePropertyTypes.RoguePropertyDefinition +): { [number]: RoguePropertyTypes.RoguePropertyDefinition } local RoguePropertyTableDefinition = (require :: any)("RoguePropertyTableDefinition") local RoguePropertyDefinition = (require :: any)("RoguePropertyDefinition") @@ -103,7 +124,7 @@ function RoguePropertyArrayUtils.createDefinitionsFromContainer(container: Insta definition = RoguePropertyDefinition.new() definition:SetName(item.Name) definition:SetParentPropertyTableDefinition(parentPropertyTableDefinition) - definition:SetDefaultValue(item.Value) + definition:SetDefaultValue((item :: any).Value) end value[index] = definition @@ -112,8 +133,8 @@ function RoguePropertyArrayUtils.createDefinitionsFromContainer(container: Insta return value end -function RoguePropertyArrayUtils.getDefaultValueMapFromContainer(container: Instance) - local value = {} +function RoguePropertyArrayUtils.getDefaultValueMapFromContainer(container: Instance): { [any]: any } + local value: { [any]: any } = {} -- This is a hack, kinda for attributeKey, attributeValue in container:GetAttributes() do @@ -128,13 +149,13 @@ function RoguePropertyArrayUtils.getDefaultValueMapFromContainer(container: Inst if item:IsA("Folder") then value[index] = RoguePropertyArrayUtils.getDefaultValueMapFromContainer(item) elseif item:IsA("ValueBase") then - value[index] = item.Value + value[index] = (item :: any).Value end else if item:IsA("Folder") then value[item.Name] = RoguePropertyArrayUtils.getDefaultValueMapFromContainer(item) else - value[item.Name] = item.Value + value[item.Name] = (item :: any).Value end end end @@ -142,7 +163,10 @@ function RoguePropertyArrayUtils.getDefaultValueMapFromContainer(container: Inst return value end -function RoguePropertyArrayUtils.createDefinitionsFromArrayData(arrayData, propertyTableDefinition) +function RoguePropertyArrayUtils.createDefinitionsFromArrayData( + arrayData: { any }, + propertyTableDefinition: RoguePropertyTypes.RoguePropertyDefinition +): { [number]: RoguePropertyTypes.RoguePropertyDefinition } local RoguePropertyTableDefinition = (require :: any)("RoguePropertyTableDefinition") local RoguePropertyDefinition = (require :: any)("RoguePropertyDefinition") diff --git a/src/rogue-properties/src/Shared/Cache/RoguePropertyCache.lua b/src/rogue-properties/src/Shared/Cache/RoguePropertyCache.lua index 4aaaf6965c..1078dc4158 100644 --- a/src/rogue-properties/src/Shared/Cache/RoguePropertyCache.lua +++ b/src/rogue-properties/src/Shared/Cache/RoguePropertyCache.lua @@ -1,4 +1,4 @@ ---!nonstrict +--!strict --[=[ Utility class that helps cache rogue properties per an instance @@ -11,11 +11,22 @@ local RoguePropertyCache = {} RoguePropertyCache.ClassName = "RoguePropertyCache" RoguePropertyCache.__index = RoguePropertyCache -function RoguePropertyCache.new(debugName: string) - local self = setmetatable({}, RoguePropertyCache) +-- A cached entry is either a RogueProperty or a RoguePropertyTable. Those types +-- form a require cycle with this module (Definition -> CacheService -> ... -> +-- Property/Table -> Definition), so the cache stores them structurally as `any`. +export type RoguePropertyCache = typeof(setmetatable( + {} :: { + _debugName: string, + _cache: { [Instance]: any }, + }, + {} :: typeof({ __index = RoguePropertyCache }) +)) + +function RoguePropertyCache.new(debugName: string): RoguePropertyCache + local self: RoguePropertyCache = setmetatable({} :: any, RoguePropertyCache) self._debugName = debugName - self._cache = setmetatable({}, WEAK_KV) + self._cache = setmetatable({}, WEAK_KV) :: any return self end @@ -26,7 +37,7 @@ end @param adornee Instance @param roguePropertyTable RoguePropertyTable ]=] -function RoguePropertyCache:Store(adornee: Instance, roguePropertyTable) +function RoguePropertyCache.Store(self: RoguePropertyCache, adornee: Instance, roguePropertyTable: any) assert(typeof(adornee) == "Instance", "Bad adornee") self._cache[adornee] = roguePropertyTable @@ -38,7 +49,7 @@ end @param adornee Instance @return RoguePropertyTable ]=] -function RoguePropertyCache:Find(adornee: Instance) +function RoguePropertyCache.Find(self: RoguePropertyCache, adornee: Instance): any assert(typeof(adornee) == "Instance", "Bad adornee") return self._cache[adornee] diff --git a/src/rogue-properties/src/Shared/Cache/RoguePropertyCacheService.lua b/src/rogue-properties/src/Shared/Cache/RoguePropertyCacheService.lua index 973ceea9ab..545e2850c9 100644 --- a/src/rogue-properties/src/Shared/Cache/RoguePropertyCacheService.lua +++ b/src/rogue-properties/src/Shared/Cache/RoguePropertyCacheService.lua @@ -1,4 +1,4 @@ ---!nonstrict +--!strict --[=[ Constructing a new rogue property/rogue property table can be expensive. This caches it so frame-usage is cheap. @@ -10,6 +10,7 @@ local require = require(script.Parent.loader).load(script) local RunService = game:GetService("RunService") local RoguePropertyCache = require("RoguePropertyCache") +local RoguePropertyTypes = require("RoguePropertyTypes") local ServiceBag = require("ServiceBag") local RoguePropertyCacheService = {} @@ -17,14 +18,25 @@ RoguePropertyCacheService.ServiceName = "RoguePropertyCacheService" local WEAK_K_TABLE = { __mode = "k" } -function RoguePropertyCacheService:Init(serviceBag: ServiceBag.ServiceBag) - assert(not self._serviceBag, "Already initialized") +export type RoguePropertyCacheService = typeof(setmetatable( + {} :: { + _serviceBag: ServiceBag.ServiceBag, + _cache: { [RoguePropertyTypes.RoguePropertyDefinition]: RoguePropertyCache.RoguePropertyCache }, + }, + {} :: typeof({ __index = RoguePropertyCacheService }) +)) + +function RoguePropertyCacheService.Init(self: RoguePropertyCacheService, serviceBag: ServiceBag.ServiceBag) + assert(not (self :: any)._serviceBag, "Already initialized") self._serviceBag = assert(serviceBag, "No serviceBag") - self._cache = setmetatable({}, WEAK_K_TABLE) + self._cache = setmetatable({}, WEAK_K_TABLE) :: any end -function RoguePropertyCacheService:GetCache(roguePropertyDefinition) +function RoguePropertyCacheService.GetCache( + self: RoguePropertyCacheService, + roguePropertyDefinition: RoguePropertyTypes.RoguePropertyDefinition +): RoguePropertyCache.RoguePropertyCache if not self._cache then assert(not RunService:IsRunning(), "Not in test mode") -- Test mode diff --git a/src/rogue-properties/src/Shared/Definition/RoguePropertyDefinition.lua b/src/rogue-properties/src/Shared/Definition/RoguePropertyDefinition.lua index 880100e188..5bca451424 100644 --- a/src/rogue-properties/src/Shared/Definition/RoguePropertyDefinition.lua +++ b/src/rogue-properties/src/Shared/Definition/RoguePropertyDefinition.lua @@ -1,4 +1,4 @@ ---!nonstrict +--!strict --[=[ @class RoguePropertyDefinition ]=] @@ -17,28 +17,42 @@ local RoguePropertyDefinition = {} RoguePropertyDefinition.ClassName = "RoguePropertyDefinition" RoguePropertyDefinition.__index = RoguePropertyDefinition -function RoguePropertyDefinition.new() - local self = setmetatable({}, RoguePropertyDefinition) +export type RoguePropertyDefinition = typeof(setmetatable( + {} :: { + _name: string, + _defaultValue: any, + _valueType: string, + _storageType: string, + _encodedDefaultValue: any, + -- Back-reference to the parent RoguePropertyTableDefinition; that class + -- requires this module (cycle) and is still nonstrict, so it is `any`. + _parentPropertyTableDefinition: any, + }, + {} :: typeof({ __index = RoguePropertyDefinition }) +)) + +function RoguePropertyDefinition.new(): RoguePropertyDefinition + local self: RoguePropertyDefinition = setmetatable({} :: any, RoguePropertyDefinition) self._name = "Unnamed" return self end -function RoguePropertyDefinition:SetDefaultValue(defaultValue) +function RoguePropertyDefinition.SetDefaultValue(self: RoguePropertyDefinition, defaultValue: any) assert(defaultValue ~= nil, "Bad defaultValue") self._defaultValue = defaultValue self._valueType = typeof(self._defaultValue) self._storageType = self:_computeStorageInstanceType() - self._encodedDefaultValue = RoguePropertyUtils.encodeProperty(self, self._defaultValue) + self._encodedDefaultValue = RoguePropertyUtils.encodeProperty(self :: any, self._defaultValue) end function RoguePropertyDefinition.isRoguePropertyDefinition(value: any): boolean return DuckTypeUtils.isImplementation(RoguePropertyDefinition, value) end -function RoguePropertyDefinition:HasChildren(): boolean +function RoguePropertyDefinition.HasChildren(_self: RoguePropertyDefinition): boolean return false end @@ -47,11 +61,17 @@ end @param adornee Instance @return RogueProperty ]=] -function RoguePropertyDefinition:Get(serviceBag: ServiceBag.ServiceBag, adornee: Instance) +function RoguePropertyDefinition.Get( + self: RoguePropertyDefinition, + serviceBag: ServiceBag.ServiceBag, + adornee: Instance +): RogueProperty.RogueProperty assert(ServiceBag.isServiceBag(serviceBag), "Bad serviceBag") assert(typeof(adornee) == "Instance", "Bad adornee") - local cacheService = serviceBag:GetService(RoguePropertyCacheService) + -- Cast: duplicate nested node_modules copies produce a spurious cyclic + -- "Expected 'RoguePropertyCacheService', got 'RoguePropertyCacheService'". + local cacheService = serviceBag:GetService(RoguePropertyCacheService) :: any local cache = cacheService:GetCache(self) local found = cache:Find(adornee) if found then @@ -64,35 +84,42 @@ function RoguePropertyDefinition:Get(serviceBag: ServiceBag.ServiceBag, adornee: return rogueProperty end -function RoguePropertyDefinition:GetOrCreateInstance(parent: Instance): Instance +function RoguePropertyDefinition.GetOrCreateInstance(self: RoguePropertyDefinition, parent: Instance): Instance assert(typeof(parent) == "Instance", "Bad parent") -- Note, in forcing the creation, we move to an attribute local original = parent:GetAttribute(self:GetName()) local created = ValueBaseUtils.getOrCreateValue( parent, - self:GetStorageInstanceType(), + self:GetStorageInstanceType() :: any, self:GetName(), self:GetEncodedDefaultValue() ) if original ~= nil and original ~= RoguePropertyConstants.INSTANCE_ATTRIBUTE_VALUE then - created.Value = original + (created :: any).Value = original end parent:SetAttribute(self:GetName(), RoguePropertyConstants.INSTANCE_ATTRIBUTE_VALUE) return created end -function RoguePropertyDefinition:SetParentPropertyTableDefinition(parentPropertyTableDefinition) +function RoguePropertyDefinition.SetParentPropertyTableDefinition( + self: RoguePropertyDefinition, + parentPropertyTableDefinition: any +) self._parentPropertyTableDefinition = parentPropertyTableDefinition end -function RoguePropertyDefinition:GetParentPropertyDefinition() +function RoguePropertyDefinition.GetParentPropertyDefinition(self: RoguePropertyDefinition): any return self._parentPropertyTableDefinition end -function RoguePropertyDefinition:CanAssign(value, _strict): (boolean, string?) +function RoguePropertyDefinition.CanAssign( + self: RoguePropertyDefinition, + value: any, + _strict: boolean? +): (boolean, string?) if self._valueType == typeof(value) then return true else @@ -106,7 +133,7 @@ function RoguePropertyDefinition:CanAssign(value, _strict): (boolean, string?) end end -function RoguePropertyDefinition:SetName(name: string): () +function RoguePropertyDefinition.SetName(self: RoguePropertyDefinition, name: string): () assert(type(name) == "string", "Bad name") self._name = name @@ -116,7 +143,7 @@ end Gets the name of the rogue property @return string ]=] -function RoguePropertyDefinition:GetName(): string +function RoguePropertyDefinition.GetName(self: RoguePropertyDefinition): string return self._name end @@ -124,7 +151,7 @@ end Gets the full name of the rogue property @return string ]=] -function RoguePropertyDefinition:GetFullName(): string +function RoguePropertyDefinition.GetFullName(self: RoguePropertyDefinition): string if self._parentPropertyTableDefinition then return self._parentPropertyTableDefinition:GetFullName() .. "." .. self._name else @@ -136,23 +163,23 @@ end Gets the default value for the property @return TProperty ]=] -function RoguePropertyDefinition:GetDefaultValue() +function RoguePropertyDefinition.GetDefaultValue(self: RoguePropertyDefinition): any return self._defaultValue end -function RoguePropertyDefinition:GetValueType() +function RoguePropertyDefinition.GetValueType(self: RoguePropertyDefinition): string return self._valueType end -function RoguePropertyDefinition:GetStorageInstanceType(): string +function RoguePropertyDefinition.GetStorageInstanceType(self: RoguePropertyDefinition): string return self._storageType end -function RoguePropertyDefinition:GetEncodedDefaultValue() - return rawget(self, "_encodedDefaultValue") +function RoguePropertyDefinition.GetEncodedDefaultValue(self: RoguePropertyDefinition): any + return rawget(self :: any, "_encodedDefaultValue") end -function RoguePropertyDefinition:_computeStorageInstanceType() +function RoguePropertyDefinition._computeStorageInstanceType(self: RoguePropertyDefinition): string if self._valueType == "string" then return "StringValue" elseif self._valueType == "table" then diff --git a/src/rogue-properties/src/Shared/Definition/RoguePropertyDefinitionArrayHelper.lua b/src/rogue-properties/src/Shared/Definition/RoguePropertyDefinitionArrayHelper.lua index 36856bbad3..2c6583ed5d 100644 --- a/src/rogue-properties/src/Shared/Definition/RoguePropertyDefinitionArrayHelper.lua +++ b/src/rogue-properties/src/Shared/Definition/RoguePropertyDefinitionArrayHelper.lua @@ -1,4 +1,4 @@ ---!nonstrict +--!strict --[=[ @class RoguePropertyDefinitionArrayHelper ]=] @@ -11,8 +11,25 @@ local RoguePropertyDefinitionArrayHelper = {} RoguePropertyDefinitionArrayHelper.ClassName = "RoguePropertyDefinitionArrayHelper" RoguePropertyDefinitionArrayHelper.__index = RoguePropertyDefinitionArrayHelper -function RoguePropertyDefinitionArrayHelper.new(propertyTableDefinition, defaultArrayData, requiredPropertyDefinition) - local self = setmetatable({}, RoguePropertyDefinitionArrayHelper) +-- propertyTableDefinition / requiredPropertyDefinition are RogueProperty*Definition +-- objects which form require cycles with this module and are still nonstrict, so +-- they (and the array data) are typed `any`. +export type RoguePropertyDefinitionArrayHelper = typeof(setmetatable( + {} :: { + _propertyTableDefinition: any, + _defaultArrayData: { any }, + _requiredPropertyDefinition: any, + _defaultDefinitions: { [number]: any }?, + }, + {} :: typeof({ __index = RoguePropertyDefinitionArrayHelper }) +)) + +function RoguePropertyDefinitionArrayHelper.new( + propertyTableDefinition: any, + defaultArrayData: { any }, + requiredPropertyDefinition: any +): RoguePropertyDefinitionArrayHelper + local self: RoguePropertyDefinitionArrayHelper = setmetatable({} :: any, RoguePropertyDefinitionArrayHelper) self._propertyTableDefinition = assert(propertyTableDefinition, "No propertyTableDefinition") self._defaultArrayData = assert(defaultArrayData, "No defaultArrayData") @@ -21,35 +38,42 @@ function RoguePropertyDefinitionArrayHelper.new(propertyTableDefinition, default return self end -function RoguePropertyDefinitionArrayHelper:IsArray(): boolean +function RoguePropertyDefinitionArrayHelper.IsArray(self: RoguePropertyDefinitionArrayHelper): boolean return self._defaultArrayData ~= nil end -function RoguePropertyDefinitionArrayHelper:GetDefaultArrayData() +function RoguePropertyDefinitionArrayHelper.GetDefaultArrayData(self: RoguePropertyDefinitionArrayHelper): { any } return self._defaultArrayData end -function RoguePropertyDefinitionArrayHelper:GetPropertyTableDefinition() +function RoguePropertyDefinitionArrayHelper.GetPropertyTableDefinition(self: RoguePropertyDefinitionArrayHelper): any return self._propertyTableDefinition end -function RoguePropertyDefinitionArrayHelper:GetDefaultDefinitions() +function RoguePropertyDefinitionArrayHelper.GetDefaultDefinitions( + self: RoguePropertyDefinitionArrayHelper +): { [number]: any } if self._defaultDefinitions then return self._defaultDefinitions end - self._defaultDefinitions = + local defaultDefinitions = RoguePropertyArrayUtils.createDefinitionsFromArrayData(self._defaultArrayData, self._propertyTableDefinition) - return self._defaultDefinitions + self._defaultDefinitions = defaultDefinitions + return defaultDefinitions end -function RoguePropertyDefinitionArrayHelper:GetRequiredPropertyDefinition() +function RoguePropertyDefinitionArrayHelper.GetRequiredPropertyDefinition(self: RoguePropertyDefinitionArrayHelper): any return self._requiredPropertyDefinition end -function RoguePropertyDefinitionArrayHelper:CanAssign(arrayValue, strict): boolean +function RoguePropertyDefinitionArrayHelper.CanAssign( + self: RoguePropertyDefinitionArrayHelper, + arrayValue: any, + strict: boolean? +): (boolean, string?) if type(arrayValue) ~= "table" then - return false, string.format("got %q, expected %q", self._valueType, typeof(arrayValue)) + return false, string.format("got %q, expected %q", (self :: any)._valueType, typeof(arrayValue)) end for key, value in arrayValue do @@ -70,7 +94,11 @@ function RoguePropertyDefinitionArrayHelper:CanAssign(arrayValue, strict): boole return true end -function RoguePropertyDefinitionArrayHelper:CanAssignAsArrayMember(value, strict): boolean +function RoguePropertyDefinitionArrayHelper.CanAssignAsArrayMember( + self: RoguePropertyDefinitionArrayHelper, + value: any, + strict: boolean +): (boolean, string?) assert(type(strict) == "boolean", "Bad strict") return self._requiredPropertyDefinition:CanAssign(value, strict) diff --git a/src/rogue-properties/src/Shared/Definition/RoguePropertyTableDefinition.lua b/src/rogue-properties/src/Shared/Definition/RoguePropertyTableDefinition.lua index ae90afce15..62c74b2b37 100644 --- a/src/rogue-properties/src/Shared/Definition/RoguePropertyTableDefinition.lua +++ b/src/rogue-properties/src/Shared/Definition/RoguePropertyTableDefinition.lua @@ -1,4 +1,4 @@ ---!nonstrict +--!strict --[=[ This holds the definition for a variety of tables. @class RoguePropertyTableDefinition @@ -21,8 +21,22 @@ local RoguePropertyTableDefinition = {} -- Inherits from RoguePropertyDefinition RoguePropertyTableDefinition.ClassName = "RoguePropertyTableDefinition" RoguePropertyTableDefinition.__index = RoguePropertyTableDefinition -function RoguePropertyTableDefinition.new(tableName: string?, defaultValueTable: Table.Map?) - local self = setmetatable(RoguePropertyDefinition.new(), RoguePropertyTableDefinition) +export type RoguePropertyTableDefinition = + typeof(setmetatable( + {} :: { + _definitionMap: { [string]: any }, + _arrayDefinitionHelper: RoguePropertyDefinitionArrayHelper.RoguePropertyDefinitionArrayHelper?, + }, + {} :: typeof({ __index = RoguePropertyTableDefinition }) + )) + & RoguePropertyDefinition.RoguePropertyDefinition + +function RoguePropertyTableDefinition.new( + tableName: string?, + defaultValueTable: Table.Map? +): RoguePropertyTableDefinition + local self: RoguePropertyTableDefinition = + setmetatable(RoguePropertyDefinition.new() :: any, RoguePropertyTableDefinition) if tableName then self:SetName(tableName) @@ -35,14 +49,17 @@ function RoguePropertyTableDefinition.new(tableName: string?, defaultValueTable: return self end -function RoguePropertyTableDefinition.isRoguePropertyTableDefinition(value): boolean +function RoguePropertyTableDefinition.isRoguePropertyTableDefinition(value: any): boolean return DuckTypeUtils.isImplementation(RoguePropertyTableDefinition, value) end -function RoguePropertyTableDefinition:SetDefaultValue(defaultValueTable: Table.Map?) +function RoguePropertyTableDefinition.SetDefaultValue( + self: RoguePropertyTableDefinition, + defaultValueTable: Table.Map? +) assert(type(defaultValueTable) == "table", "Bad defaultValueTable") - RoguePropertyDefinition.SetDefaultValue(self, defaultValueTable) + RoguePropertyDefinition.SetDefaultValue(self :: any, defaultValueTable) self._definitionMap = {} @@ -76,8 +93,11 @@ function RoguePropertyTableDefinition:SetDefaultValue(defaultValueTable: Table.M RoguePropertyArrayUtils.createRequiredPropertyDefinitionFromArray(defaultArrayData, self) if requiredPropertyDefinitionTemplate then - self._arrayDefinitionHelper = - RoguePropertyDefinitionArrayHelper.new(self, defaultArrayData, requiredPropertyDefinitionTemplate) + self._arrayDefinitionHelper = RoguePropertyDefinitionArrayHelper.new( + self :: any, + defaultArrayData, + requiredPropertyDefinitionTemplate + ) else error( string.format( @@ -89,7 +109,11 @@ function RoguePropertyTableDefinition:SetDefaultValue(defaultValueTable: Table.M end end -function RoguePropertyTableDefinition:CanAssign(mainValue, strict: boolean): (boolean, string?) +function RoguePropertyTableDefinition.CanAssign( + self: RoguePropertyTableDefinition, + mainValue: any, + strict: boolean +): (boolean, string?) assert(type(strict) == "boolean", "Bad strict") if type(mainValue) ~= "table" then @@ -172,15 +196,15 @@ function RoguePropertyTableDefinition:CanAssign(mainValue, strict: boolean): (bo return true, nil end -function RoguePropertyTableDefinition:GetDefinitionArrayHelper() +function RoguePropertyTableDefinition.GetDefinitionArrayHelper(self: RoguePropertyTableDefinition): any return self._arrayDefinitionHelper end -function RoguePropertyTableDefinition:GetDefinitionMap() +function RoguePropertyTableDefinition.GetDefinitionMap(self: RoguePropertyTableDefinition): { [string]: any } return self._definitionMap end -function RoguePropertyTableDefinition:HasChildren() +function RoguePropertyTableDefinition.HasChildren(_self: RoguePropertyTableDefinition): boolean return true end @@ -189,10 +213,10 @@ end @param propertyName @return RoguePropertyDefinition? ]=] -function RoguePropertyTableDefinition:GetDefinition(propertyName: string) +function RoguePropertyTableDefinition.GetDefinition(self: RoguePropertyTableDefinition, propertyName: string): any assert(type(propertyName) == "string", "Bad propertyName") - local definitions = rawget(self, "_definitionMap") + local definitions = rawget(self :: any, "_definitionMap") :: any return definitions[propertyName] end @@ -204,11 +228,17 @@ end @param adornee Instance @return RoguePropertyTable ]=] -function RoguePropertyTableDefinition:Get(serviceBag: ServiceBag.ServiceBag, adornee: Instance) +function RoguePropertyTableDefinition.Get( + self: RoguePropertyTableDefinition, + serviceBag: ServiceBag.ServiceBag, + adornee: Instance +): any assert(ServiceBag.isServiceBag(serviceBag), "Bad serviceBag") assert(typeof(adornee) == "Instance", "Bad adornee") - local cacheService = serviceBag:GetService(RoguePropertyCacheService) + -- Cast: duplicate nested node_modules copies produce a spurious cyclic + -- "Expected 'RoguePropertyCacheService', got 'RoguePropertyCacheService'". + local cacheService = serviceBag:GetService(RoguePropertyCacheService) :: any local cache = cacheService:GetCache(self) local found = cache:Find(adornee) if found then @@ -220,7 +250,9 @@ function RoguePropertyTableDefinition:Get(serviceBag: ServiceBag.ServiceBag, ado if not self:GetParentPropertyDefinition() then -- Set default value for top level only - roguePropertyTable:SetCanInitialize(serviceBag:GetService(RoguePropertyService):CanInitializeProperties()) + roguePropertyTable:SetCanInitialize( + (serviceBag:GetService(RoguePropertyService) :: any):CanInitializeProperties() + ) end return roguePropertyTable @@ -233,7 +265,11 @@ RoguePropertyTableDefinition.GetPropertyTable = RoguePropertyTableDefinition.Get @return Observable> ]=] -function RoguePropertyTableDefinition:ObserveContainerBrio(serviceBag: ServiceBag.ServiceBag, adornee: Instance) +function RoguePropertyTableDefinition.ObserveContainerBrio( + self: RoguePropertyTableDefinition, + serviceBag: ServiceBag.ServiceBag, + adornee: Instance +): any assert(serviceBag, "No serviceBag") assert(typeof(adornee) == "Instance", "Bad adornee") @@ -248,7 +284,11 @@ end Gets the current container for the given adornee. @return Folder? ]=] -function RoguePropertyTableDefinition:GetContainer(serviceBag: ServiceBag.ServiceBag, adornee: Instance): Folder? +function RoguePropertyTableDefinition.GetContainer( + self: RoguePropertyTableDefinition, + serviceBag: ServiceBag.ServiceBag, + adornee: Instance +): Folder? assert(serviceBag, "No serviceBag") assert(typeof(adornee) == "Instance", "Bad adornee") @@ -257,18 +297,18 @@ function RoguePropertyTableDefinition:GetContainer(serviceBag: ServiceBag.Servic return found:GetContainer() end -function RoguePropertyTableDefinition:FindInstance(parent: Instance): Instance? +function RoguePropertyTableDefinition.FindInstance(self: RoguePropertyTableDefinition, parent: Instance): Instance? assert(typeof(parent) == "Instance", "Bad parent") return parent:FindFirstChild(self:GetName()) end -function RoguePropertyTableDefinition:GetOrCreateInstance(parent: Instance): Folder +function RoguePropertyTableDefinition.GetOrCreateInstance(self: RoguePropertyTableDefinition, parent: Instance): Folder assert(typeof(parent) == "Instance", "Bad parent") local existing = parent:FindFirstChild(self:GetName()) if existing then - return existing + return existing :: any end local folder = Instance.new("Folder") @@ -277,17 +317,19 @@ function RoguePropertyTableDefinition:GetOrCreateInstance(parent: Instance): Fol return folder end -function RoguePropertyTableDefinition:__index(index: string) +local rawRoguePropertyTableDefinition = RoguePropertyTableDefinition :: any + +rawRoguePropertyTableDefinition.__index = function(self: RoguePropertyTableDefinition, index: any): any assert(type(index) == "string", "Bad index") if index == "_definitionMap" or index == "_arrayDefinitionHelper" or index == "_parentPropertyTableDefinition" then - return rawget(self, index) - elseif RoguePropertyTableDefinition[index] then - return RoguePropertyTableDefinition[index] - elseif RoguePropertyDefinition[index] then - return RoguePropertyDefinition[index] + return rawget(self :: any, index) + elseif rawRoguePropertyTableDefinition[index] then + return rawRoguePropertyTableDefinition[index] + elseif (RoguePropertyDefinition :: any)[index] then + return (RoguePropertyDefinition :: any)[index] elseif type(index) == "string" then - local definitions = rawget(self, "_definitionMap") + local definitions = rawget(self :: any, "_definitionMap") :: any if definitions[index] then return definitions[index] @@ -295,7 +337,7 @@ function RoguePropertyTableDefinition:__index(index: string) error(string.format("Bad definition %q", tostring(index))) end elseif type(index) == "number" then - local definitionArrayHelper = rawget(self, "_arrayDefinitionHelper") + local definitionArrayHelper = rawget(self :: any, "_arrayDefinitionHelper") if definitionArrayHelper then local defaultDefinitions = definitionArrayHelper:GetDefaultDefinitions() if defaultDefinitions then diff --git a/src/rogue-properties/src/Shared/Implementation/RogueProperty.lua b/src/rogue-properties/src/Shared/Implementation/RogueProperty.lua index e7015936e9..b574fae4b0 100644 --- a/src/rogue-properties/src/Shared/Implementation/RogueProperty.lua +++ b/src/rogue-properties/src/Shared/Implementation/RogueProperty.lua @@ -1,4 +1,4 @@ ---!nonstrict +--!strict --[=[ @class RogueProperty ]=] @@ -34,24 +34,46 @@ local RogueProperty = {} RogueProperty.ClassName = "RogueProperty" RogueProperty.__index = RogueProperty -function RogueProperty.new(adornee: Instance, serviceBag: ServiceBag.ServiceBag, definition) +-- Generic over the value type T. Like ValueObject, RogueProperty exposes `.Value` +-- and `.Changed` through a *function* `__index`/`__newindex` (assigned below); they are +-- declared here as virtual fields so callers can read them and so T is load-bearing. +-- Methods are derived from the class table via `__index = RogueProperty`. `_definition` +-- is the RoguePropertyDefinition back-reference across the require cycle (those classes +-- require this module and are still nonstrict), so it stays `any`. +export type RogueProperty = typeof(setmetatable( + {} :: { + Value: T, + Changed: any, + + _serviceBag: ServiceBag.ServiceBag, + _tieRealmService: TieRealmService.TieRealmService, + _adornee: Instance, + _definition: any, + _canInitialize: boolean, + }, + {} :: typeof({ __index = RogueProperty }) +)) + +function RogueProperty.new(adornee: Instance, serviceBag: ServiceBag.ServiceBag, definition: any): RogueProperty local self = {} self._serviceBag = assert(serviceBag, "No serviceBag") - self._tieRealmService = self._serviceBag:GetService(TieRealmService) + -- Cast: duplicate nested node_modules copies of TieRealmService produce a + -- spurious "Expected 'TieRealmService', got 'TieRealmService'" cyclic error. + self._tieRealmService = self._serviceBag:GetService(TieRealmService) :: any self._adornee = assert(adornee, "Bad adornee") self._definition = assert(definition, "Bad definition") self._canInitialize = false - return setmetatable(self, RogueProperty) + return setmetatable(self, RogueProperty) :: any end -function RogueProperty:SetCanInitialize(canInitialize: boolean) +function RogueProperty.SetCanInitialize(self: RogueProperty, canInitialize: boolean) assert(type(canInitialize) == "boolean", "Bad canInitialize") - if rawget(self, "_canInitialize") ~= canInitialize then - rawset(self, "_canInitialize", canInitialize) + if rawget(self :: any, "_canInitialize") ~= canInitialize then + rawset(self :: any, "_canInitialize", canInitialize) if canInitialize then self:GetBaseValueObject(RoguePropertyBaseValueTypes.ANY) @@ -59,15 +81,15 @@ function RogueProperty:SetCanInitialize(canInitialize: boolean) end end -function RogueProperty:GetAdornee() +function RogueProperty.GetAdornee(self: RogueProperty): Instance return self._adornee end -function RogueProperty:CanInitialize() - return rawget(self, "_canInitialize") +function RogueProperty.CanInitialize(self: RogueProperty): boolean + return rawget(self :: any, "_canInitialize") :: boolean end -function RogueProperty:_getParentContainer() +function RogueProperty._getParentContainer(self: RogueProperty): Instance? local parentDefinition = self._definition:GetParentPropertyDefinition() if parentDefinition then return parentDefinition:Get(self._serviceBag, self._adornee):GetContainer() @@ -76,25 +98,25 @@ function RogueProperty:_getParentContainer() end end -function RogueProperty:GetBaseValueObject(roguePropertyBaseValueType) +function RogueProperty.GetBaseValueObject(self: RogueProperty, roguePropertyBaseValueType: any): any assert( RoguePropertyBaseValueTypeUtils.isRoguePropertyBaseValueType(roguePropertyBaseValueType), "Bad roguePropertyBaseValueType" ) -- TODO: check this caching! - local cachedInstance = rawget(self, "_baseValueInstanceCache") - local adornee = rawget(self, "_adornee") + local cachedInstance = rawget(self :: any, "_baseValueInstanceCache") + local adornee = rawget(self :: any, "_adornee") if cachedInstance then if cachedInstance:IsDescendantOf(adornee) then return cachedInstance else - rawset(self, "_baseValueInstanceCache", nil) + rawset(self :: any, "_baseValueInstanceCache", nil) end end - local definition = rawget(self, "_definition") + local definition = rawget(self :: any, "_definition") :: any local parent = self:_getParentContainer() if not parent then @@ -123,7 +145,7 @@ function RogueProperty:GetBaseValueObject(roguePropertyBaseValueType) end if found then - rawset(self, "_baseValueInstanceCache", found) + rawset(self :: any, "_baseValueInstanceCache", found) parent:SetAttribute(definition:GetName(), RoguePropertyConstants.INSTANCE_ATTRIBUTE_VALUE) return found elseif not instanceRequired then @@ -141,8 +163,8 @@ function RogueProperty:GetBaseValueObject(roguePropertyBaseValueType) end end -function RogueProperty:_observeParentBrio() - local cache = rawget(self, "_observeParentBrioCache") +function RogueProperty._observeParentBrio(self: RogueProperty): any + local cache = rawget(self :: any, "_observeParentBrioCache") if cache then return cache end @@ -156,13 +178,13 @@ function RogueProperty:_observeParentBrio() cache = RxBrioUtils.of(self._adornee) end - rawset(self, "_observeParentBrioCache", cache) + rawset(self :: any, "_observeParentBrioCache", cache) return cache end -function RogueProperty:_observeBaseValueBrio() - local cache = rawget(self, "_observeBaseValueBrioCache") +function RogueProperty._observeBaseValueBrio(self: RogueProperty): any + local cache = rawget(self :: any, "_observeBaseValueBrioCache") if cache then return cache end @@ -178,12 +200,12 @@ function RogueProperty:_observeBaseValueBrio() Rx.cache(), }) - rawset(self, "_observeBaseValueBrioCache", cache) + rawset(self :: any, "_observeBaseValueBrioCache", cache) return cache end -function RogueProperty:SetBaseValue(value) +function RogueProperty.SetBaseValue(self: RogueProperty, value: T) assert(self._definition:CanAssign(value, false)) -- This has a good error message local baseValue = self:GetBaseValueObject(RoguePropertyBaseValueTypes.ANY) @@ -200,7 +222,7 @@ function RogueProperty:SetBaseValue(value) end end -function RogueProperty:_getModifierParentContainerForNewModifier() +function RogueProperty._getModifierParentContainerForNewModifier(self: RogueProperty): Instance? if self:CanInitialize() then return self:GetBaseValueObject(RoguePropertyBaseValueTypes.INSTANCE) end @@ -235,11 +257,11 @@ function RogueProperty:_getModifierParentContainerForNewModifier() return localParent end -function RogueProperty:_getLocalModifierParentName() +function RogueProperty._getLocalModifierParentName(self: RogueProperty): string return self._definition:GetName() .. "_LocalModifiers" end -function RogueProperty:_getModifierParentContainerList() +function RogueProperty._getModifierParentContainerList(self: RogueProperty): { Instance } local containerList = {} if self:CanInitialize() then @@ -262,7 +284,7 @@ function RogueProperty:_getModifierParentContainerList() return containerList end -function RogueProperty:PromiseBaseValue() +function RogueProperty.PromiseBaseValue(self: RogueProperty): any return Rx.toPromise(self:_observeBaseValueBrio():Pipe({ RxBrioUtils.flattenToValueAndNil, Rx.where(function(value) @@ -271,16 +293,16 @@ function RogueProperty:PromiseBaseValue() })) end -function RogueProperty:_observeModifierContainersBrio() +function RogueProperty._observeModifierContainersBrio(self: RogueProperty): any local name = self:_getLocalModifierParentName() return self:_observeParentBrio():Pipe({ RxBrioUtils.switchMapBrio(function(parent) return Rx.merge({ -- The main container - RxAttributeUtils.observeAttributeBrio(parent, self._definition:GetName(), function(attribute) + (RxAttributeUtils.observeAttributeBrio(parent, self._definition:GetName(), function(attribute) return attribute == RoguePropertyConstants.INSTANCE_ATTRIBUTE_VALUE - end):Pipe({ + end) :: any):Pipe({ Rx.switchMap(function() return self:_observeBaseValueBrio() end), @@ -290,12 +312,12 @@ function RogueProperty:_observeModifierContainersBrio() RxInstanceUtils.observeChildrenBrio(parent, function(child) return child:IsA(LOCAL_MODIFIER_CONTAINER_CLASS_NAME) and child.Name == name end), - }) + } :: { any }) end), }) end -function RogueProperty:SetValue(value) +function RogueProperty.SetValue(self: RogueProperty, value: T) assert(self._definition:CanAssign(value, false)) -- This has a good error message local baseValue = self:GetBaseValueObject(RoguePropertyBaseValueTypes.ANY) @@ -337,7 +359,7 @@ function RogueProperty:SetValue(value) baseValue.Value = self:_encodeValue(current) end -function RogueProperty:GetBaseValue() +function RogueProperty.GetBaseValue(self: RogueProperty): T local baseValue = self:GetBaseValueObject(RoguePropertyBaseValueTypes.ANY) if baseValue then return self:_decodeValue(baseValue.Value) @@ -346,7 +368,7 @@ function RogueProperty:GetBaseValue() end end -function RogueProperty:GetValue() +function RogueProperty.GetValue(self: RogueProperty): T local propObj = self:GetBaseValueObject(RoguePropertyBaseValueTypes.ANY) if not propObj then return self._definition:GetDefaultValue() @@ -361,11 +383,11 @@ function RogueProperty:GetValue() return current end -function RogueProperty:GetDefinition() +function RogueProperty.GetDefinition(self: RogueProperty): any return self._definition end -function RogueProperty:GetRogueModifiers() +function RogueProperty.GetRogueModifiers(self: RogueProperty): { any } local modifierList = {} for _, parent in self:_getModifierParentContainerList() do @@ -389,13 +411,13 @@ function RogueProperty:GetRogueModifiers() return modifierList end -function RogueProperty:_observeModifierSortedList() - local cache = rawget(self, "_observeModifierSortedListCache") +function RogueProperty._observeModifierSortedList(self: RogueProperty): any + local cache = rawget(self :: any, "_observeModifierSortedListCache") if cache then return cache end - cache = Observable.new(function(sub) + cache = (Observable.new(function(sub) local topMaid = Maid.new() local sortedList = topMaid:Add(ObservableSortedList.new()) @@ -420,16 +442,16 @@ function RogueProperty:_observeModifierSortedList() debug.profileend() return topMaid - end):Pipe({ + end) :: any):Pipe({ Rx.cache(), }) - rawset(self, "_observeModifierSortedListCache", cache) + rawset(self :: any, "_observeModifierSortedListCache", cache) return cache end -function RogueProperty:Observe() - local cache = rawget(self, "_observeCache") +function RogueProperty.Observe(self: RogueProperty): Observable.Observable + local cache = rawget(self :: any, "_observeCache") :: any if cache then return cache end @@ -456,7 +478,7 @@ function RogueProperty:Observe() RxBrioUtils.emitOnDeath(self._definition:GetDefaultValue()), Rx.defaultsTo(self._definition:GetDefaultValue()), Rx.distinct(), - }) + } :: { any }) cache = self:_observeModifierSortedList():Pipe({ Rx.switchMap(function(sortedList) @@ -470,18 +492,18 @@ function RogueProperty:Observe() return current end), Rx.cache(), - }) - rawset(self, "_observeCache", cache) + } :: { any }) + rawset(self :: any, "_observeCache", cache) return cache end -function RogueProperty:ObserveBrio(predicate) - return self:Observe():Pipe({ +function RogueProperty.ObserveBrio(self: RogueProperty, predicate: ((T) -> boolean)?): any + return (self:Observe() :: any):Pipe({ RxBrioUtils.switchToBrio(predicate), }) end -function RogueProperty:CreateMultiplier(amount: number, source): Instance? +function RogueProperty.CreateMultiplier(self: RogueProperty, amount: number, source: Instance?): Instance? assert(type(amount) == "number", "Bad amount") local className = ValueBaseUtils.getClassNameFromType(typeof(amount)) @@ -490,7 +512,7 @@ function RogueProperty:CreateMultiplier(amount: number, source): Instance? return nil end - local multiplier = Instance.new(className) + local multiplier = Instance.new(className) :: any multiplier.Name = "Multiplier" multiplier.Value = amount @@ -505,7 +527,7 @@ function RogueProperty:CreateMultiplier(amount: number, source): Instance? return multiplier end -function RogueProperty:CreateAdditive(amount: number, source) +function RogueProperty.CreateAdditive(self: RogueProperty, amount: number, source: Instance?): Instance? assert(type(amount) == "number", "Bad amount") local className = ValueBaseUtils.getClassNameFromType(typeof(amount)) @@ -514,7 +536,7 @@ function RogueProperty:CreateAdditive(amount: number, source) return nil end - local additive = Instance.new(className) + local additive = Instance.new(className) :: any additive.Name = "Additive" additive.Value = amount @@ -529,7 +551,7 @@ function RogueProperty:CreateAdditive(amount: number, source) return additive end -function RogueProperty:GetNamedAdditive(name, source) +function RogueProperty.GetNamedAdditive(self: RogueProperty, name: string, source: Instance?): Instance? local modifierParent = self:_getModifierParentContainerForNewModifier() if not modifierParent then -- TODO: Handle this parenting scenario appropriately @@ -552,19 +574,19 @@ function RogueProperty:GetNamedAdditive(name, source) return found end - local created = self:CreateAdditive(0, source) + local created = self:CreateAdditive(0, source) :: any created.Name = searchName return created end -function RogueProperty:CreateSetter(value, source) +function RogueProperty.CreateSetter(self: RogueProperty, value: any, source: Instance?): Instance? local className = ValueBaseUtils.getClassNameFromType(typeof(value)) if not className then error(string.format("[RogueProperty.CreateSetter] - Can't set to type %q", typeof(value))) return nil end - local setter = Instance.new(className) + local setter = Instance.new(className) :: any setter.Name = "Setter" setter.Value = value @@ -579,7 +601,7 @@ function RogueProperty:CreateSetter(value, source) return setter end -function RogueProperty:_parentModifier(modifier: Instance) +function RogueProperty._parentModifier(self: RogueProperty, modifier: Instance) local modifierParent = self:_getModifierParentContainerForNewModifier() if modifierParent then modifier.Parent = modifierParent @@ -622,40 +644,42 @@ function RogueProperty:_parentModifier(modifier: Instance) return end -function RogueProperty:__index(index) - if RogueProperty[index] then - return RogueProperty[index] +local rawRogueProperty = RogueProperty :: any + +rawRogueProperty.__index = function(self: RogueProperty, index: any): any + if rawRogueProperty[index] then + return rawRogueProperty[index] elseif index == "Value" then return self:GetValue() elseif index == "Changed" then - return self:GetChangedEvent() + return (self :: any):GetChangedEvent() else error(string.format("Bad index %q", tostring(index))) end end -function RogueProperty:__newindex(index, value) +rawRogueProperty.__newindex = function(self: RogueProperty, index: any, value: any) if index == "Value" then self:SetValue(value) elseif index == "Changed" then error("Cannot set .Changed event") - elseif RogueProperty[index] then + elseif rawRogueProperty[index] then error(string.format("Cannot set %q", tostring(index))) else error(string.format("Bad index %q", tostring(index))) end end -function RogueProperty:_decodeValue(current) +function RogueProperty._decodeValue(self: RogueProperty, current: any): any return RoguePropertyUtils.decodeProperty(self._definition, current) end -function RogueProperty:_encodeValue(current) +function RogueProperty._encodeValue(self: RogueProperty, current: any): any return RoguePropertyUtils.encodeProperty(self._definition, current) end -function RogueProperty:GetChangedEvent() - return RxSignal.new(self:Observe():Pipe({ +function RogueProperty.GetChangedEvent(self: RogueProperty): any + return RxSignal.new((self:Observe() :: any):Pipe({ Rx.skip(1), })) end diff --git a/src/rogue-properties/src/Shared/Implementation/RoguePropertyArrayHelper.lua b/src/rogue-properties/src/Shared/Implementation/RoguePropertyArrayHelper.lua index 35e03305e1..24e00c38f6 100644 --- a/src/rogue-properties/src/Shared/Implementation/RoguePropertyArrayHelper.lua +++ b/src/rogue-properties/src/Shared/Implementation/RoguePropertyArrayHelper.lua @@ -1,4 +1,4 @@ ---!nonstrict +--!strict --[=[ @class RoguePropertyArrayHelper ]=] @@ -6,6 +6,7 @@ local require = require(script.Parent.loader).load(script) local BaseObject = require("BaseObject") +local Observable = require("Observable") local RoguePropertyArrayUtils = require("RoguePropertyArrayUtils") local RoguePropertyBaseValueTypes = require("RoguePropertyBaseValueTypes") local Rx = require("Rx") @@ -15,8 +16,28 @@ local RoguePropertyArrayHelper = setmetatable({}, BaseObject) RoguePropertyArrayHelper.ClassName = "RoguePropertyArrayHelper" RoguePropertyArrayHelper.__index = RoguePropertyArrayHelper -function RoguePropertyArrayHelper.new(serviceBag: ServiceBag.ServiceBag, arrayDefinitionHelper, roguePropertyTable) - local self = setmetatable(BaseObject.new(), RoguePropertyArrayHelper) +-- _roguePropertyTable (a RoguePropertyTable), _arrayDefinitionHelper (a +-- RoguePropertyDefinitionArrayHelper), and the RogueProperty instances they +-- vend all form require cycles with this module, and several are still nonstrict. +-- They are typed `any` here; this helper is internal plumbing, not public surface. +export type RoguePropertyArrayHelper = + typeof(setmetatable( + {} :: { + _serviceBag: ServiceBag.ServiceBag, + _roguePropertyTable: any, + _arrayDefinitionHelper: any, + _defaultRogueProperties: { any }?, + }, + {} :: typeof({ __index = RoguePropertyArrayHelper }) + )) + & BaseObject.BaseObject + +function RoguePropertyArrayHelper.new( + serviceBag: ServiceBag.ServiceBag, + arrayDefinitionHelper: any, + roguePropertyTable: any +): RoguePropertyArrayHelper + local self: RoguePropertyArrayHelper = setmetatable(BaseObject.new() :: any, RoguePropertyArrayHelper) self._serviceBag = assert(serviceBag, "No serviceBag") self._roguePropertyTable = assert(roguePropertyTable, "No roguePropertyTable") @@ -25,13 +46,13 @@ function RoguePropertyArrayHelper.new(serviceBag: ServiceBag.ServiceBag, arrayDe return self end -function RoguePropertyArrayHelper:SetCanInitialize(canInitialize: boolean) +function RoguePropertyArrayHelper.SetCanInitialize(self: RoguePropertyArrayHelper, canInitialize: boolean) if canInitialize then self:GetArrayRogueProperties() end end -function RoguePropertyArrayHelper:GetArrayRogueProperty(index: number) +function RoguePropertyArrayHelper.GetArrayRogueProperty(self: RoguePropertyArrayHelper, index: number): any assert(type(index) == "number", "Bad index") -- TODO: Maybe return something general for that index @@ -41,7 +62,7 @@ function RoguePropertyArrayHelper:GetArrayRogueProperty(index: number) return rogueProperties[index] end -function RoguePropertyArrayHelper:GetArrayRogueProperties() +function RoguePropertyArrayHelper.GetArrayRogueProperties(self: RoguePropertyArrayHelper): { [number]: any } -- Dynamic construction of the properties based upon when exists local container = self._roguePropertyTable:GetContainer() @@ -79,12 +100,12 @@ function RoguePropertyArrayHelper:GetArrayRogueProperties() return rogueProperties end -function RoguePropertyArrayHelper:_getDefaultRogueProperties() +function RoguePropertyArrayHelper._getDefaultRogueProperties(self: RoguePropertyArrayHelper): { any } if self._defaultRogueProperties then return self._defaultRogueProperties end - local defaultRogueProperties = {} + local defaultRogueProperties: { any } = {} local adornee = self._roguePropertyTable:GetAdornee() for _, definition in self._arrayDefinitionHelper:GetDefaultDefinitions() do local property = definition:Get(self._serviceBag, adornee) @@ -92,10 +113,10 @@ function RoguePropertyArrayHelper:_getDefaultRogueProperties() end self._defaultRogueProperties = defaultRogueProperties - return self._defaultRogueProperties + return defaultRogueProperties end -function RoguePropertyArrayHelper:SetArrayBaseData(arrayData) +function RoguePropertyArrayHelper.SetArrayBaseData(self: RoguePropertyArrayHelper, arrayData: { any }) assert(self._arrayDefinitionHelper:CanAssign(arrayData, false)) -- This has good error messages local container = self._roguePropertyTable:GetContainer() @@ -135,7 +156,7 @@ function RoguePropertyArrayHelper:SetArrayBaseData(arrayData) self:_removeUnspecified(container, definitions) end -function RoguePropertyArrayHelper:SetArrayData(arrayData) +function RoguePropertyArrayHelper.SetArrayData(self: RoguePropertyArrayHelper, arrayData: { any }) assert(self._arrayDefinitionHelper:CanAssign(arrayData, false)) -- This has good error messages local container = self._roguePropertyTable:GetContainer() @@ -174,7 +195,11 @@ function RoguePropertyArrayHelper:SetArrayData(arrayData) self:_removeUnspecified(container, definitions) end -function RoguePropertyArrayHelper:_removeUnspecified(container, definitions) +function RoguePropertyArrayHelper._removeUnspecified( + _self: RoguePropertyArrayHelper, + container: Instance, + definitions: { [number]: any } +) for _, item in container:GetChildren() do local index = RoguePropertyArrayUtils.getIndexFromName(item.Name) if index then @@ -185,7 +210,7 @@ function RoguePropertyArrayHelper:_removeUnspecified(container, definitions) end end -function RoguePropertyArrayHelper:GetArrayBaseValues() +function RoguePropertyArrayHelper.GetArrayBaseValues(self: RoguePropertyArrayHelper): { [number]: any } local result = {} for index, rogueProperty in pairs(self:GetArrayRogueProperties()) do result[index] = rogueProperty:GetBaseValue() @@ -193,7 +218,7 @@ function RoguePropertyArrayHelper:GetArrayBaseValues() return result end -function RoguePropertyArrayHelper:GetArrayValues() +function RoguePropertyArrayHelper.GetArrayValues(self: RoguePropertyArrayHelper): { [number]: any } local result = {} for index, rogueProperty in pairs(self:GetArrayRogueProperties()) do result[index] = rogueProperty:GetValue() @@ -201,7 +226,7 @@ function RoguePropertyArrayHelper:GetArrayValues() return result end -function RoguePropertyArrayHelper:ObserveArrayValues() +function RoguePropertyArrayHelper.ObserveArrayValues(self: RoguePropertyArrayHelper): Observable.Observable --warn("[RoguePropertyArrayHelper] - Observing arrays is only partially supported") -- TODO: Allow for observing diff --git a/src/rogue-properties/src/Shared/Implementation/RoguePropertyTable.lua b/src/rogue-properties/src/Shared/Implementation/RoguePropertyTable.lua index 02e0a5cefc..89222274d7 100644 --- a/src/rogue-properties/src/Shared/Implementation/RoguePropertyTable.lua +++ b/src/rogue-properties/src/Shared/Implementation/RoguePropertyTable.lua @@ -1,10 +1,11 @@ ---!nonstrict +--!strict --[=[ @class RoguePropertyTable ]=] local require = require(script.Parent.loader).load(script) +local Observable = require("Observable") local RogueProperty = require("RogueProperty") local RoguePropertyArrayHelper = require("RoguePropertyArrayHelper") local Rx = require("Rx") @@ -16,38 +17,56 @@ local RoguePropertyTable = {} -- inherits from RogueProperty RoguePropertyTable.ClassName = "RoguePropertyTable" RoguePropertyTable.__index = RoguePropertyTable -function RoguePropertyTable.new(adornee: Instance, serviceBag: ServiceBag.ServiceBag, roguePropertyTableDefinition) - local self = setmetatable(RogueProperty.new(adornee, serviceBag, roguePropertyTableDefinition), RoguePropertyTable) - - rawset(self, "_properties", {}) +-- Built on a RogueProperty (shares its fields/methods via the intersection). Holds a +-- table of child properties, so it is RogueProperty<{ [any]: any }>. The dynamic +-- `.Value`/`.Changed`/child-property access goes through the function `__index` below. +export type RoguePropertyTable = + typeof(setmetatable( + {} :: { + _properties: { [string]: any }, + _arrayHelper: any, + }, + {} :: typeof({ __index = RoguePropertyTable }) + )) + & RogueProperty.RogueProperty<{ [any]: any }> + +function RoguePropertyTable.new( + adornee: Instance, + serviceBag: ServiceBag.ServiceBag, + roguePropertyTableDefinition: any +): RoguePropertyTable + local self: RoguePropertyTable = + setmetatable(RogueProperty.new(adornee, serviceBag, roguePropertyTableDefinition) :: any, RoguePropertyTable) + + rawset(self :: any, "_properties", {}) local arrayDefinitionHelper = self:GetDefinition():GetDefinitionArrayHelper() if arrayDefinitionHelper then - rawset(self, "_arrayHelper", RoguePropertyArrayHelper.new(serviceBag, arrayDefinitionHelper, self)) + rawset(self :: any, "_arrayHelper", RoguePropertyArrayHelper.new(serviceBag, arrayDefinitionHelper, self)) end return self end -function RoguePropertyTable:SetCanInitialize(canInitialize: boolean) +function RoguePropertyTable.SetCanInitialize(self: RoguePropertyTable, canInitialize: boolean) assert(type(canInitialize) == "boolean", "Bad canInitialize") if self:CanInitialize() ~= canInitialize then - RogueProperty.SetCanInitialize(self, canInitialize) + RogueProperty.SetCanInitialize(self :: any, canInitialize) for _, property in self:GetRogueProperties() do property:SetCanInitialize(canInitialize) end - local arrayHelper = rawget(self, "_arrayHelper") + local arrayHelper = rawget(self :: any, "_arrayHelper") if arrayHelper then arrayHelper:SetCanInitialize(canInitialize) end end end -function RoguePropertyTable:ObserveContainerBrio() - local cache = rawget(self, "_observeContainerCache") +function RoguePropertyTable.ObserveContainerBrio(self: RoguePropertyTable): any + local cache = rawget(self :: any, "_observeContainerCache") if cache then return cache end @@ -60,30 +79,30 @@ function RoguePropertyTable:ObserveContainerBrio() parentTable:GetContainer() end - cache = parentTable:ObserveContainerBrio():Pipe({ + cache = (parentTable:ObserveContainerBrio() :: any):Pipe({ RxBrioUtils.switchMapBrio(function(parent) return RxInstanceUtils.observeLastNamedChildBrio(parent, "Folder", self._definition:GetName()) end), Rx.cache(), }) else - cache = RxInstanceUtils.observeLastNamedChildBrio(self._adornee, "Folder", self._definition:GetName()):Pipe({ + cache = (RxInstanceUtils.observeLastNamedChildBrio(self._adornee, "Folder", self._definition:GetName()) :: any):Pipe({ Rx.cache(), }) end cache = cache - rawset(self, "_observeContainerCache", cache) + rawset(self :: any, "_observeContainerCache", cache) return cache end -function RoguePropertyTable:GetContainer(): Instance? - local cached = rawget(self, "_containerCache") +function RoguePropertyTable.GetContainer(self: RoguePropertyTable): Instance? + local cached = rawget(self :: any, "_containerCache") if cached then if cached:IsDescendantOf(self._adornee) then return cached else - rawset(self, "_containerCache", nil) + rawset(self :: any, "_containerCache", nil) end end @@ -107,11 +126,11 @@ function RoguePropertyTable:GetContainer(): Instance? container = self._definition:FindInstance(parent) end - rawset(self, "_containerCache", container) + rawset(self :: any, "_containerCache", container) return container end -function RoguePropertyTable:SetBaseValue(newBaseValue) +function RoguePropertyTable.SetBaseValue(self: RoguePropertyTable, newBaseValue: any) assert(self._definition:CanAssign(newBaseValue, false)) -- This has a good error message local arrayData = {} @@ -130,7 +149,7 @@ function RoguePropertyTable:SetBaseValue(newBaseValue) end if next(arrayData) ~= nil then - local arrayHelper = rawget(self, "_arrayHelper") + local arrayHelper = rawget(self :: any, "_arrayHelper") if arrayHelper then arrayHelper:SetArrayBaseData(arrayData) else @@ -139,7 +158,7 @@ function RoguePropertyTable:SetBaseValue(newBaseValue) end end -function RoguePropertyTable:SetValue(newValue) +function RoguePropertyTable.SetValue(self: RoguePropertyTable, newValue: any) assert(self._definition:CanAssign(newValue, false)) -- This has a good error message local arrayData = {} @@ -158,7 +177,7 @@ function RoguePropertyTable:SetValue(newValue) end if next(arrayData) ~= nil then - local arrayHelper = rawget(self, "_arrayHelper") + local arrayHelper = rawget(self :: any, "_arrayHelper") if arrayHelper then arrayHelper:SetArrayData(arrayData) else @@ -167,8 +186,8 @@ function RoguePropertyTable:SetValue(newValue) end end -function RoguePropertyTable:GetRogueProperties() - local arrayHelper = rawget(self, "_arrayHelper") +function RoguePropertyTable.GetRogueProperties(self: RoguePropertyTable): { [any]: any } + local arrayHelper = rawget(self :: any, "_arrayHelper") local properties = arrayHelper and arrayHelper:GetArrayRogueProperties() or {} for propertyName, rogueDefinition in pairs(self._definition:GetDefinitionMap()) do @@ -183,8 +202,8 @@ function RoguePropertyTable:GetRogueProperties() return properties end -function RoguePropertyTable:GetValue() - local arrayHelper = rawget(self, "_arrayHelper") +function RoguePropertyTable.GetValue(self: RoguePropertyTable): { [any]: any } + local arrayHelper = rawget(self :: any, "_arrayHelper") local values = arrayHelper and arrayHelper:GetArrayValues() or {} for key, rogueDefinition in pairs(self._definition:GetDefinitionMap()) do @@ -197,8 +216,8 @@ function RoguePropertyTable:GetValue() return values end -function RoguePropertyTable:GetBaseValue() - local arrayHelper = rawget(self, "_arrayHelper") +function RoguePropertyTable.GetBaseValue(self: RoguePropertyTable): { [any]: any } + local arrayHelper = rawget(self :: any, "_arrayHelper") local values = arrayHelper and arrayHelper:GetArrayBaseValues() or {} for key, rogueDefinition in pairs(self._definition:GetDefinitionMap()) do @@ -211,8 +230,8 @@ function RoguePropertyTable:GetBaseValue() return values end -function RoguePropertyTable:Observe() - local arrayHelper = rawget(self, "_arrayHelper") +function RoguePropertyTable.Observe(self: RoguePropertyTable): Observable.Observable + local arrayHelper = rawget(self :: any, "_arrayHelper") if arrayHelper then return arrayHelper:ObserveArrayValues() end @@ -220,9 +239,9 @@ function RoguePropertyTable:Observe() return self:_observeDictionary() end -function RoguePropertyTable:_observeDictionary() +function RoguePropertyTable._observeDictionary(self: RoguePropertyTable): any -- ok, this is definitely slow - local cache = rawget(self, "_observeDictionaryCache") + local cache = rawget(self :: any, "_observeDictionaryCache") if cache then return cache end @@ -241,17 +260,17 @@ function RoguePropertyTable:_observeDictionary() if next(toObserve) == nil then cache = Rx.of({}) else - cache = Rx.combineLatest(toObserve):Pipe({ + cache = (Rx.combineLatest(toObserve) :: any):Pipe({ Rx.cache(), }) end - rawset(self, "_observeDictionaryCache", cache) + rawset(self :: any, "_observeDictionaryCache", cache) return cache end -function RoguePropertyTable:GetRogueProperty(name: string) +function RoguePropertyTable.GetRogueProperty(self: RoguePropertyTable, name: string): any assert(type(name) == "string", "Bad name") -- Caching these things doesn't do a whole lot, but saves on table allocation. @@ -271,12 +290,14 @@ function RoguePropertyTable:GetRogueProperty(name: string) end end -function RoguePropertyTable:__newindex(index, value) +local rawRoguePropertyTable = RoguePropertyTable :: any + +rawRoguePropertyTable.__newindex = function(self: RoguePropertyTable, index: any, value: any) if index == "Value" then self:SetValue(value) elseif index == "Changed" then error("Cannot set .Changed event") - elseif RoguePropertyTable[index] then + elseif rawRoguePropertyTable[index] then error(string.format("Cannot set %q on %s", tostring(index), self._definition:GetFullName())) elseif type(index) == "string" then local property = self:GetRogueProperty(index) @@ -290,13 +311,13 @@ function RoguePropertyTable:__newindex(index, value) end end -function RoguePropertyTable:__index(index) +rawRoguePropertyTable.__index = function(self: RoguePropertyTable, index: any): any assert(type(index) == "string" or type(index) == "number", "Bad index") - if RoguePropertyTable[index] then - return RoguePropertyTable[index] - elseif rawget(RogueProperty, index) ~= nil then - return rawget(RogueProperty, index) + if rawRoguePropertyTable[index] then + return rawRoguePropertyTable[index] + elseif rawget(RogueProperty :: any, index) ~= nil then + return rawget(RogueProperty :: any, index) elseif index == "Value" then return self:GetValue() elseif index == "Changed" then @@ -308,7 +329,7 @@ function RoguePropertyTable:__index(index) end return property elseif type(index) == "number" then - local arrayHelper = rawget(self, "_arrayHelper") + local arrayHelper = rawget(self :: any, "_arrayHelper") if arrayHelper then local result = arrayHelper:GetArrayRogueProperty(index) diff --git a/src/rogue-properties/src/Shared/Implementation/RoguePropertyUtils.lua b/src/rogue-properties/src/Shared/Implementation/RoguePropertyUtils.lua index 54aa65c5bb..5c9fda2055 100644 --- a/src/rogue-properties/src/Shared/Implementation/RoguePropertyUtils.lua +++ b/src/rogue-properties/src/Shared/Implementation/RoguePropertyUtils.lua @@ -1,4 +1,4 @@ ---!nonstrict +--!strict --[=[ @class RoguePropertyUtils ]=] @@ -6,10 +6,11 @@ local require = require(script.Parent.loader).load(script) local JSONUtils = require("JSONUtils") +local RoguePropertyTypes = require("RoguePropertyTypes") local RoguePropertyUtils = {} -function RoguePropertyUtils.decodeProperty(definition, value) +function RoguePropertyUtils.decodeProperty(definition: RoguePropertyTypes.RoguePropertyDefinition, value: any): any if definition:GetValueType() == "table" then local ok, decoded, err = JSONUtils.jsonDecode(value) if not ok then @@ -23,7 +24,7 @@ function RoguePropertyUtils.decodeProperty(definition, value) end end -function RoguePropertyUtils.encodeProperty(definition, value) +function RoguePropertyUtils.encodeProperty(definition: RoguePropertyTypes.RoguePropertyDefinition, value: any): any if definition:GetValueType() == "table" then local ok, encoded, err = JSONUtils.jsonEncode(value) if not ok then diff --git a/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueAdditive.lua b/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueAdditive.lua index a9b0952167..e5c7d28498 100644 --- a/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueAdditive.lua +++ b/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueAdditive.lua @@ -1,4 +1,4 @@ ---!nonstrict +--!strict --[=[ @class RogueAdditive ]=] @@ -7,6 +7,7 @@ local require = require(script.Parent.loader).load(script) local Binder = require("Binder") local LinearValue = require("LinearValue") +local Observable = require("Observable") local RogueModifierBase = require("RogueModifierBase") local RogueModifierInterface = require("RogueModifierInterface") local Rx = require("Rx") @@ -17,43 +18,50 @@ local RogueAdditive = setmetatable({}, RogueModifierBase) RogueAdditive.ClassName = "RogueAdditive" RogueAdditive.__index = RogueAdditive -function RogueAdditive.new(valueObject, serviceBag: ServiceBag.ServiceBag) - local self = setmetatable(RogueModifierBase.new(valueObject, serviceBag), RogueAdditive) +export type RogueAdditive = + typeof(setmetatable({} :: {}, {} :: typeof({ __index = RogueAdditive }))) + & RogueModifierBase.RogueModifierBase - self._maid:GiveTask(RogueModifierInterface:Implement(self._obj, self, self._tieRealmService:GetTieRealm())) +function RogueAdditive.new(valueObject: ValueBase, serviceBag: ServiceBag.ServiceBag): RogueAdditive + local self: RogueAdditive = setmetatable(RogueModifierBase.new(valueObject, serviceBag) :: any, RogueAdditive) + + self._maid:GiveTask(RogueModifierInterface:Implement(self._obj :: any, self, self._tieRealmService:GetTieRealm())) return self end -function RogueAdditive:GetModifiedVersion(value) +function RogueAdditive.GetModifiedVersion(self: RogueAdditive, value: any): any if not self._data.Enabled.Value then return value end local input = LinearValue.toLinearIfNeeded(value) - local additive = LinearValue.toLinearIfNeeded(self._obj.Value) + local additive = LinearValue.toLinearIfNeeded((self._obj :: any).Value) - return LinearValue.fromLinearIfNeeded(input + additive) + return LinearValue.fromLinearIfNeeded((input :: any) + additive) end -function RogueAdditive:GetInvertedVersion(value) +function RogueAdditive.GetInvertedVersion(self: RogueAdditive, value: any): any if not self._data.Enabled.Value then return value end local input = LinearValue.toLinearIfNeeded(value) - local additive = LinearValue.toLinearIfNeeded(self._obj.Value) + local additive = LinearValue.toLinearIfNeeded((self._obj :: any).Value) - return LinearValue.fromLinearIfNeeded(input - additive) + return LinearValue.fromLinearIfNeeded((input :: any) - additive) end -function RogueAdditive:ObserveModifiedVersion(inputValue) - return Rx.combineLatest({ +function RogueAdditive.ObserveModifiedVersion( + self: RogueAdditive, + inputValue: Observable.Observable +): Observable.Observable + return (Rx.combineLatest({ inputValue = inputValue, enabled = self._data.Enabled:Observe(), - additive = RxInstanceUtils.observeProperty(self._obj, "Value"), - }):Pipe({ - Rx.map(function(state) + additive = RxInstanceUtils.observeProperty(self._obj :: any, "Value"), + }) :: any):Pipe({ + Rx.map(function(state: any) if not state.enabled then return state.inputValue end @@ -62,13 +70,13 @@ function RogueAdditive:ObserveModifiedVersion(inputValue) local input = LinearValue.toLinearIfNeeded(state.inputValue) local additive = LinearValue.toLinearIfNeeded(state.additive) - return LinearValue.fromLinearIfNeeded(input + additive) + return LinearValue.fromLinearIfNeeded((input :: any) + additive) else return state.inputValue end end), Rx.distinct(), - }) + } :: { any }) :: any end -return Binder.new("RogueAdditive", RogueAdditive) +return Binder.new("RogueAdditive", RogueAdditive :: any) :: Binder.Binder diff --git a/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueModifierBase.lua b/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueModifierBase.lua index 910f09848e..49eda3b58b 100644 --- a/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueModifierBase.lua +++ b/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueModifierBase.lua @@ -1,4 +1,4 @@ ---!nonstrict +--!strict --[=[ @class RogueModifierBase ]=] @@ -9,16 +9,34 @@ local BaseObject = require("BaseObject") local RoguePropertyModifierData = require("RoguePropertyModifierData") local ServiceBag = require("ServiceBag") local TieRealmService = require("TieRealmService") +local ValueObject = require("ValueObject") local RogueModifierBase = setmetatable({}, BaseObject) RogueModifierBase.ClassName = "RogueModifierBase" RogueModifierBase.__index = RogueModifierBase -function RogueModifierBase.new(obj, serviceBag: ServiceBag.ServiceBag) - local self = setmetatable(BaseObject.new(obj), RogueModifierBase) +export type RogueModifierBase = + typeof(setmetatable( + {} :: { + _serviceBag: ServiceBag.ServiceBag, + _tieRealmService: TieRealmService.TieRealmService, + -- _data is the AdorneeData "create" result for RoguePropertyModifierData, + -- which is a nonstrict module exporting no type. + _data: any, + Order: ValueObject.ValueObject, + Source: ValueObject.ValueObject, + }, + {} :: typeof({ __index = RogueModifierBase }) + )) + & BaseObject.BaseObject + +function RogueModifierBase.new(obj: ValueBase, serviceBag: ServiceBag.ServiceBag): RogueModifierBase + local self: RogueModifierBase = setmetatable(BaseObject.new(obj) :: any, RogueModifierBase) self._serviceBag = assert(serviceBag, "No serviceBag") - self._tieRealmService = self._serviceBag:GetService(TieRealmService) + -- Cast: duplicate nested node_modules copies of TieRealmService produce a + -- spurious "Expected 'TieRealmService', got 'TieRealmService'" cyclic error. + self._tieRealmService = self._serviceBag:GetService(TieRealmService) :: any self._data = RoguePropertyModifierData:Create(self._obj) @@ -28,11 +46,11 @@ function RogueModifierBase.new(obj, serviceBag: ServiceBag.ServiceBag) return self end -function RogueModifierBase:GetModifiedVersion(_value) +function RogueModifierBase.GetModifiedVersion(_self: RogueModifierBase, _value: any): any error("Not implemented") end -function RogueModifierBase:ObserveModifiedVersion(_value) +function RogueModifierBase.ObserveModifiedVersion(_self: RogueModifierBase, _value: any): any error("Not implemented") end diff --git a/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueMultiplier.lua b/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueMultiplier.lua index 3ac08fa9bc..38ca0f65ff 100644 --- a/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueMultiplier.lua +++ b/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueMultiplier.lua @@ -1,4 +1,4 @@ ---!nonstrict +--!strict --[=[ @class RogueMultiplier ]=] @@ -7,6 +7,7 @@ local require = require(script.Parent.loader).load(script) local Binder = require("Binder") local LinearValue = require("LinearValue") +local Observable = require("Observable") local RogueModifierBase = require("RogueModifierBase") local RogueModifierInterface = require("RogueModifierInterface") local Rx = require("Rx") @@ -17,43 +18,50 @@ local RogueMultiplier = setmetatable({}, RogueModifierBase) RogueMultiplier.ClassName = "RogueMultiplier" RogueMultiplier.__index = RogueMultiplier -function RogueMultiplier.new(valueObject, serviceBag: ServiceBag.ServiceBag) - local self = setmetatable(RogueModifierBase.new(valueObject, serviceBag), RogueMultiplier) +export type RogueMultiplier = + typeof(setmetatable({} :: {}, {} :: typeof({ __index = RogueMultiplier }))) + & RogueModifierBase.RogueModifierBase - self._maid:GiveTask(RogueModifierInterface:Implement(self._obj, self, self._tieRealmService:GetTieRealm())) +function RogueMultiplier.new(valueObject: ValueBase, serviceBag: ServiceBag.ServiceBag): RogueMultiplier + local self: RogueMultiplier = setmetatable(RogueModifierBase.new(valueObject, serviceBag) :: any, RogueMultiplier) + + self._maid:GiveTask(RogueModifierInterface:Implement(self._obj :: any, self, self._tieRealmService:GetTieRealm())) return self end -function RogueMultiplier:GetModifiedVersion(value) +function RogueMultiplier.GetModifiedVersion(self: RogueMultiplier, value: any): any if not self._data.Enabled.Value then return value end local input = LinearValue.toLinearIfNeeded(value) - local multiplier = LinearValue.toLinearIfNeeded(self._obj.Value) + local multiplier = LinearValue.toLinearIfNeeded((self._obj :: any).Value) - return LinearValue.fromLinearIfNeeded(input * multiplier) + return LinearValue.fromLinearIfNeeded((input :: any) * multiplier) end -function RogueMultiplier:GetInvertedVersion(value) +function RogueMultiplier.GetInvertedVersion(self: RogueMultiplier, value: any): any if not self._data.Enabled.Value then return value end local input = LinearValue.toLinearIfNeeded(value) - local multiplier = LinearValue.toLinearIfNeeded(self._obj.Value) + local multiplier = LinearValue.toLinearIfNeeded((self._obj :: any).Value) - return LinearValue.fromLinearIfNeeded(input / multiplier) + return LinearValue.fromLinearIfNeeded((input :: any) / multiplier) end -function RogueMultiplier:ObserveModifiedVersion(inputValue) - return Rx.combineLatest({ +function RogueMultiplier.ObserveModifiedVersion( + self: RogueMultiplier, + inputValue: Observable.Observable +): Observable.Observable + return (Rx.combineLatest({ inputValue = inputValue, enabled = self._data.Enabled:Observe(), - multiplier = RxInstanceUtils.observeProperty(self._obj, "Value"), - }):Pipe({ - Rx.map(function(state) + multiplier = RxInstanceUtils.observeProperty(self._obj :: any, "Value"), + }) :: any):Pipe({ + Rx.map(function(state: any) if not state.enabled then return state.inputValue end @@ -62,13 +70,13 @@ function RogueMultiplier:ObserveModifiedVersion(inputValue) local input = LinearValue.toLinearIfNeeded(state.inputValue) local multiplier = LinearValue.toLinearIfNeeded(state.multiplier) - return LinearValue.fromLinearIfNeeded(input * multiplier) + return LinearValue.fromLinearIfNeeded((input :: any) * multiplier) else return state.inputValue end end), Rx.distinct(), - }) + } :: { any }) :: any end -return Binder.new("RogueMultiplier", RogueMultiplier) +return Binder.new("RogueMultiplier", RogueMultiplier :: any) :: Binder.Binder diff --git a/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueSetter.lua b/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueSetter.lua index 1b9ad6670a..9c560a9ab9 100644 --- a/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueSetter.lua +++ b/src/rogue-properties/src/Shared/Modifiers/Implementations/RogueSetter.lua @@ -1,4 +1,4 @@ ---!nonstrict +--!strict --[=[ @class RogueSetter ]=] @@ -6,6 +6,7 @@ local require = require(script.Parent.loader).load(script) local Binder = require("Binder") +local Observable = require("Observable") local RogueModifierBase = require("RogueModifierBase") local RogueModifierInterface = require("RogueModifierInterface") local Rx = require("Rx") @@ -16,36 +17,43 @@ local RogueSetter = setmetatable({}, RogueModifierBase) RogueSetter.ClassName = "RogueSetter" RogueSetter.__index = RogueSetter -function RogueSetter.new(valueObject, serviceBag: ServiceBag.ServiceBag) - local self = setmetatable(RogueModifierBase.new(valueObject, serviceBag), RogueSetter) +export type RogueSetter = + typeof(setmetatable({} :: {}, {} :: typeof({ __index = RogueSetter }))) + & RogueModifierBase.RogueModifierBase - self._maid:GiveTask(RogueModifierInterface:Implement(self._obj, self, self._tieRealmService:GetTieRealm())) +function RogueSetter.new(valueObject: ValueBase, serviceBag: ServiceBag.ServiceBag): RogueSetter + local self: RogueSetter = setmetatable(RogueModifierBase.new(valueObject, serviceBag) :: any, RogueSetter) + + self._maid:GiveTask(RogueModifierInterface:Implement(self._obj :: any, self, self._tieRealmService:GetTieRealm())) return self end -function RogueSetter:GetModifiedVersion(value) +function RogueSetter.GetModifiedVersion(self: RogueSetter, value: any): any if self._data.Enabled.Value then - return self._obj.Value + return (self._obj :: any).Value else return value end end -function RogueSetter:ObserveModifiedVersion(inputValue) - return self._data.Enabled:Observe():Pipe({ - Rx.switchMap(function(enabled) +function RogueSetter.ObserveModifiedVersion( + self: RogueSetter, + inputValue: Observable.Observable +): Observable.Observable + return (self._data.Enabled:Observe() :: any):Pipe({ + Rx.switchMap(function(enabled: any) if enabled then - return RxValueBaseUtils.observeValue(self._obj) + return RxValueBaseUtils.observeValue(self._obj :: any) else return inputValue end end), Rx.distinct(), - }) + } :: { any }) :: any end -function RogueSetter:GetInvertedVersion(value, initialValue) +function RogueSetter.GetInvertedVersion(self: RogueSetter, value: any, initialValue: any): any if not self._data.Enabled.Value then return value end @@ -53,4 +61,4 @@ function RogueSetter:GetInvertedVersion(value, initialValue) return initialValue end -return Binder.new("RogueSetter", RogueSetter) +return Binder.new("RogueSetter", RogueSetter :: any) :: Binder.Binder diff --git a/src/rogue-properties/src/Shared/Modifiers/RoguePropertyModifierData.lua b/src/rogue-properties/src/Shared/Modifiers/RoguePropertyModifierData.lua index 6c386dc872..585164dbd3 100644 --- a/src/rogue-properties/src/Shared/Modifiers/RoguePropertyModifierData.lua +++ b/src/rogue-properties/src/Shared/Modifiers/RoguePropertyModifierData.lua @@ -1,4 +1,4 @@ ---!nonstrict +--!strict --[=[ @class RoguePropertyModifierData ]=] @@ -8,12 +8,12 @@ local require = require(script.Parent.loader).load(script) local AdorneeData = require("AdorneeData") local AdorneeDataEntry = require("AdorneeDataEntry") local ValueBaseValue = require("ValueBaseValue") -local t = require("t") +local t: any = require("t") -- t isn't strict-friendly return AdorneeData.new({ Enabled = true, Order = 0, - RoguePropertySourceLink = AdorneeDataEntry.new(t.optional(t.Instance), function(adornee) + RoguePropertySourceLink = AdorneeDataEntry.new(t.optional(t.Instance), function(adornee: Instance) return ValueBaseValue.new(adornee, "ObjectValue", "RoguePropertySourceLink", nil) end), }) diff --git a/src/rogue-properties/src/Shared/RoguePropertyService.lua b/src/rogue-properties/src/Shared/RoguePropertyService.lua index c82824a019..fa9393a2bd 100644 --- a/src/rogue-properties/src/Shared/RoguePropertyService.lua +++ b/src/rogue-properties/src/Shared/RoguePropertyService.lua @@ -1,4 +1,4 @@ ---!nonstrict +--!strict --[=[ This service handles the observable part of a rogue property which allows for us to listen for all of these additives and multipliers in a centralized location. @@ -16,8 +16,16 @@ local ServiceBag = require("ServiceBag") local RoguePropertyService = {} RoguePropertyService.ServiceName = "RoguePropertyService" -function RoguePropertyService:Init(serviceBag: ServiceBag.ServiceBag) - assert(not self._serviceBag, "Already initialized") +export type RoguePropertyService = typeof(setmetatable( + {} :: { + _serviceBag: ServiceBag.ServiceBag, + _maid: Maid.Maid, + }, + {} :: typeof({ __index = RoguePropertyService }) +)) + +function RoguePropertyService.Init(self: RoguePropertyService, serviceBag: ServiceBag.ServiceBag) + assert(not (self :: any)._serviceBag, "Already initialized") self._serviceBag = assert(serviceBag, "No serviceBag") self._maid = Maid.new() @@ -32,11 +40,11 @@ function RoguePropertyService:Init(serviceBag: ServiceBag.ServiceBag) self._serviceBag:GetService(require("RogueSetter")) end -function RoguePropertyService:CanInitializeProperties(): boolean +function RoguePropertyService.CanInitializeProperties(_self: RoguePropertyService): boolean return RunService:IsServer() end -function RoguePropertyService:Destroy() +function RoguePropertyService.Destroy(self: RoguePropertyService) self._maid:DoCleaning() end diff --git a/src/rogue-properties/src/Shared/RoguePropertyTypes.lua b/src/rogue-properties/src/Shared/RoguePropertyTypes.lua new file mode 100644 index 0000000000..9b86b04473 --- /dev/null +++ b/src/rogue-properties/src/Shared/RoguePropertyTypes.lua @@ -0,0 +1,29 @@ +--!strict +--[=[ + Shared structural types for the RoguePropertyService package. + + The RoguePropertyDefinition / RoguePropertyTableDefinition classes require the + helper and util modules in this package, so those helpers cannot import the + definition classes back without forming a require cycle (and the classes are also + still nonstrict, exporting no type of their own). This module names the slice of the + definition surface those helpers use, so they share one type instead of each + redeclaring it or falling back to `any`. Tighten here once the definition classes + export real types. + + @class RoguePropertyTypes +]=] + +local RoguePropertyTypes = {} + +-- The definition surface the array/cache/util helpers touch. `self`, the serviceBag, and +-- the minted property are `any` because their concrete types would re-form the require +-- cycle described above; the named methods/returns are what the helpers actually rely on. +export type RoguePropertyDefinition = { + Get: (self: any, serviceBag: any, adornee: Instance) -> any, + GetValueType: (self: any) -> string, + GetName: (self: any) -> string, + GetDefaultValue: (self: any) -> any, + GetEncodedDefaultValue: (self: any) -> any, +} + +return RoguePropertyTypes