rbx-safe-types
v1.0.32
Published
A drop-in replacement for rbx-types.
Downloads
13
Readme
ARCHIVED
rbx-safe-types allowed me to iterate over rbx-types ideas much faster and allowed me to experiment and figure out the best ideas for how roblox types should work. Since we have now integrated almost everything I want into rbx-types, I will no longer be maintaining this. I now recommend using the latest rbx-types, which is what I and the roblox-ts team officially support.
Although unrecommended, if you really want to take this idea for yourself, and work on it yourself, you may fork Validark/rbx-safe-types/master.
rbx-safe-types
This is a drop-in replacement for rbx-types.
Installation
Make sure you have the latest version of TypeScript installed by simply running npm i -g typescript
.
Run one of the following commands in the directory of your project. Afterwards you will need to reload the window. See the proper command for your system:
Windows
npm i rbx-safe-types & CD node_modules & MOVE /Y rbx-safe-types\include\* rbx-types\include & DEL rbx-types\include\manual.d.ts & RMDIR /Q rbx-safe-types\include & CD .. & echo Installed rbx-safe-types!
MacOS / Any OS with GNU installed
npm i rbx-safe-types & cd node_modules & mv rbx-safe-types/include/* rbx-types/include & rm rbx-types/include/manual.d.ts & rm -r rbx-safe-types/include & CD .. & echo Installed rbx-safe-types!
Restart VSCode by pressing (Ctrl+Shift+P) and selecting Reload Window
.
Differences
Here are the reasons I use rbx-safe-types over rbx-types:
rbx-safe-types allows type narrowing via ClassName
const part = {} as Instance; if (part.IsA("Part")) { // part is: Part | FlagStand | Platform | Seat | SkateboardPlatform | SpawnLocation } if (part.ClassName === "Part") { // part is: Part }
rbx-safe-types requires explicitness in specifying instance types which are instantiable and superclasses to other types. Consider the following code:
// in rbx-types, this accepts Script | LocalScript // in rbx-safe-types, this accepts only an object for which the ClassName is Script function getScriptClassName(script: Script) { return script.ClassName; } // in rbx-types, this accepts StarterPlayerScripts | StarterCharacterScripts // in rbx-safe-types, this accepts only an object for which the ClassName is StarterPlayerScripts function getObjectClassName(obj: StarterPlayerScripts) { return obj.ClassName; } // in rbx-types this accepts Model | Status | Workspace; // in rbx-safe-types, this accepts only an object for which the ClassName is Model function getModelClassName(obj: Model) { return obj.ClassName; }
Basically, in rbx-safe-types a
Script
isClassName === "Script"
and in rbx-types it isIsA("Script")
. In other words, in rbx-types aScript
is equivalent to rbx-safe-types'Script | LocalScript
. The same behavior applies to everything in theInstanceBases
interface.rbx-safe-types disallows referencing instances through the dot operator without an explicit definition. This helps avoid name collisions, and can also increases transpiler performance in cases like this. There are two alternatives, shown below:
const Workspace = game.GetService("Workspace"); const ReplicatedStorage = game.GetService("ReplicatedStorage"); // bad practice, and disallowed in rbx-safe-types: const myPart = Workspace.Maps.Valiant.Houses; // safer, won't ever have property conflicts, etc. const houses = Workspace .FindFirstChild("Maps")! .FindFirstChild("Valiant")! .FindFirstChild("Houses")!; // Another alternative is using unioned types: interface RemotesChildren { Chatted: RemoteEvent; Attacked: RemoteEvent; Usurped: RemoteEvent & { ShouldReplicate: BoolValue; }; } const remoteFolder = ReplicatedStorage.WaitForChild("Remotes") as Folder & RemotesChildren; print(remoteFolder.Chatted); // exists! Chatted is a RemoteEvent print(remoteFolder.Usurped.ShouldReplicate.Value); // TS knows that .Value is a boolean!
Consider this code, which rbx-types will improperly handle:
function f(a: Instance) { return a.JobId; } // rbx-types thinks this returns an Instance. f(game); // rbx-safe-types will give you an error, // because your function signature should accept a DataModel instead of an Instance: function g(a: DataModel) { return a.JobId; } g(game); // works! returns a string
Any time you have a type which is less specific than the actual instance, you risk running into property-instance name collisions. Thus, rbx-safe-types disallows inferring that accessing non-members are
Instance
types.rbx-safe-types does not assume that indexing arrays results in defined values.
rbx-safe-types pulls documentation from https://developer.roblox.com/api-reference and thus has far more documentation.
rbx-safe-types sometimes contains better type information about APIs that return dictionaries. For example, the fields in GetFriendsOnline which may or may not be undefined can be validated by checking the
LocationType
, which rbx-safe-types has a const enum for. rbx-safe-types also adds anAssetTypeId
const enum.rbx-safe-types has full support for BrickColors, and can be used similarly to the ClassName narrowing mentioned above if desired.
rbx-safe-types has "real" Roblox Enum object support, meaning one can access
EnumItem.Value
,EnumItem.Name
, andEnumItem.EnumType
. However, neitherEnum.GetEnums()
norEnumType.GetEnumItems()
are supported.EnumItem.Value
is recommended for those concerned with efficiency, andEnumItem.Name
is obviously better for readability and TypeScript's suggestion prompts.- This allows rbx-safe-types to add
CastsToEnum<EnumType | EnumItem>
, so that TS code can pass strings/numbers into Roblox functions which require Enums. At the moment, we are waiting on a TS update which allow us to permit this behavior on property setting. - Goal: At some point I would like to have an optimization built-in to the transpiler to fold
Enum.EnumType.EnumItem.Value
into a constant. - Caveat: As a side effect of TypeScript's type system, if working exclusively with the Enum Objects themselves, you may have to
return error("")
on an extra branch because TS considers any object that looks like a duck to be a duck. Thus, Enums are not considered immutable by TypeScript, but theirValue
andName
properties are, because strings and numbers are each immutable. - Like Roblox Objects, one should refrain from trying to iterate over their members using a for loop.
- This allows rbx-safe-types to add
rbx-safe-types has better information and is more specific about what kind of Instance things are.
rbx-safe-types doesn't have a bunch of internal members of Instances which look like:
_210: never
rbx-safe-types removes 99% of the internal
Rbx_
classes. Those that remain now carry the prefixRbxInternal
instead to avoid confusion for users.rbx-safe-types has more dank documentation:
(this documentation was copied from this page, but I thought it was particularly funny)