Use type functions for vide.create

This commit is contained in:
centauri 2025-11-26 12:35:34 +00:00
parent c16022586e
commit d7d2f5167e
2 changed files with 87 additions and 73 deletions

View file

@ -5,11 +5,9 @@ local defaults = require "./defaults"
local apply = require "./apply" local apply = require "./apply"
local flags = require "./flags" local flags = require "./flags"
local ctor_cache = {} :: { [string]: () -> Instance } local ctor_cache = {} :: { [string]: (AnyProps) -> Instance }
local function lazy_init(_, class: string)
setmetatable(ctor_cache :: any, { local function ctor(properties: AnyProps): Instance
__index = function(self, class)
local function ctor(properties: Props): Instance
local ok, instance: Instance = pcall(Instance.new, class :: any) local ok, instance: Instance = pcall(Instance.new, class :: any)
if not ok then error(`invalid class name { class }`, 0) end if not ok then error(`invalid class name { class }`, 0) end
@ -25,70 +23,86 @@ setmetatable(ctor_cache :: any, {
return apply(instance, properties) return apply(instance, properties)
end end
self[class] = ctor ctor_cache[class] = ctor
return ctor return ctor
end
})
local function create_instance(class: string)
return ctor_cache[class]
end end
setmetatable(ctor_cache, { __index = lazy_init })
local function clone_instance(instance: Instance) -- todo: remove support for different overloads
return function(properties: Props): Instance local function create(class_or_instance: string|Instance, properties: AnyProps?): ((AnyProps) -> Instance) | Instance
local clone = instance:Clone() if type(class_or_instance) ~= "string" and typeof(class_or_instance) ~= "Instance" then
error("bad argument #1, expected string or instance, got " .. typeof(class_or_instance), 0)
end
local ctor = if type(class_or_instance) == "string"
then ctor_cache[class_or_instance]
else function(properties)
local clone = class_or_instance:Clone()
if not clone then error "attempt to clone a non-archivable instance" end if not clone then error "attempt to clone a non-archivable instance" end
return apply(clone, properties) return apply(clone, properties)
end end
return if properties
then ctor(properties)
else ctor
end end
local function create(class_or_instance: string | Instance, props: Props?): ((Props) -> Instance) | Instance type Instances = {
local result: (Props) -> Instance Folder: Folder,
if type(class_or_instance) == "string" then BillboardGui: BillboardGui,
result = create_instance(class_or_instance) CanvasGroup: CanvasGroup,
elseif typeof(class_or_instance) == "Instance" then Frame: Frame,
result = clone_instance(class_or_instance) ImageButton: ImageButton,
else ImageLabel: ImageLabel,
error("bad argument #1, expected string or instance, got " .. typeof(class_or_instance), 0) ScreenGui: ScreenGui,
return nil :: never ScrollingFrame: ScrollingFrame,
SurfaceGui: SurfaceGui,
TextBox: TextBox,
TextButton: TextButton,
TextLabel: TextLabel,
UIAspectRatioConstraint: UIAspectRatioConstraint,
UICorner: UICorner,
UIGradient: UIGradient,
UIGridLayout: UIGridLayout,
UIListLayout: UIListLayout,
}
type function Properties(instance: type?)
local properties = types.newtable()
while instance do
for i, v in instance:properties() do
local connector = v.read and v.read.tag == "table" and v.read:readproperty(types.singleton("Connect"))
if connector then
if not connector then continue end
local params = connector:parameters().head
if not params then continue end
local listener = params[2]
if not listener then continue end
properties:setproperty(i, types.optional(listener))
elseif v.write then
properties:setproperty(i, types.optional(types.unionof(
v.write,
types.newfunction({}, { head = { v.write } })
)))
end end
if props then
return result(props)
end end
return result
instance = instance:readparent()
end
properties:setindexer(types.number, types.any)
return properties
end end
type Props = { [any]: any } type AnyProps = { [any]: any }
type Create = (
(Instance) -> (AnyProps) -> Instance
) & (
(string, AnyProps) -> Instance
) & (
<Name>(Name|keyof<Instances>) -> (Properties<index<Instances, Name>>) -> index<Instances, Name>
)
type Create<Name, Instance> = ((Name, Props) -> Instance) & ((Name) -> (Props) -> Instance) return (create :: any) :: Create
return (create :: any) ::
& ( <T>(T & Instance) -> (Props) -> T )
& ( <T>(T & Instance, Props) -> T )
& Create<"Folder", Folder>
& Create<"BillboardGui", BillboardGui>
& Create<"CanvasGroup", CanvasGroup>
& Create<"Frame", Frame>
& Create<"ImageButton", ImageButton>
& Create<"ImageLabel", ImageLabel>
& Create<"ScreenGui", ScreenGui>
& Create<"ScrollingFrame", ScrollingFrame>
& Create<"SurfaceGui", SurfaceGui>
& Create<"TextBox", TextBox>
& Create<"TextButton", TextButton>
& Create<"TextLabel", TextLabel>
& Create<"UIAspectRatioConstraint", UIAspectRatioConstraint>
& Create<"UICorner", UICorner>
& Create<"UIGradient", UIGradient>
& Create<"UIGridLayout", UIGridLayout>
& Create<"UIListLayout", UIListLayout>
& Create<"UIPadding", UIPadding>
& Create<"UIPageLayout", UIPageLayout>
& Create<"UIScale", UIScale>
& Create<"UISizeConstraint", UISizeConstraint>
& Create<"UIStroke", UIStroke>
& Create<"UITableLayout", UITableLayout>
& Create<"UITextSizeConstraint", UITextSizeConstraint>
& Create<"VideoFrame", VideoFrame>
& Create<"ViewportFrame", ViewportFrame>
& Create<string, Instance>

View file

@ -46,7 +46,7 @@ end
local vide = require "../src/" local vide = require "../src/"
local count = vide.source(0) local count = vide.source(0)
create("TextButton") { create("TextButton") { -- todo: why is `:: "TextButton"` not necessary?
BackgroundTransparency = 1, BackgroundTransparency = 1,
AnchorPoint = "bad value", -- should error AnchorPoint = "bad value", -- should error
InvalidProperty = true, -- should error InvalidProperty = true, -- should error