Refactor Instance Constructors
See original GitHub issueCurrently, instances are constructed in the form of new T([parent]), where T is the class name
I’d like to propose switching to the format of: new Instance(className[, parent])
Reasons why I think new Part() is a bad design:
- Namespace collisions
In some contexts, you cannot create variables of the name
Partdue to it already being reserved by a class. However, this class doesn’t really “exist” as a value. - Extending Classes
We have a bug where users can create classes in the form of
class Foo extends Part {}. Bugs like this were introduced due to thenew Part()design using classes which add to the value namespace. - Using Instance Classes as Values
Users can pass around these magic Instance classes as values. For example,
foo(Part);. This doesn’t really make sense from a design perspective, sincePartisn’t actually a value. We attempted to fix this using magic instance constructor values, but I think that goes against what users expect that value might be. - Learning Curve
We tell users that they should use
new T()in place ofT.new(), but for Instances there is an exception. I think switching this behavior tonew Instance("Part")will make more sense to new users and is more on parity with the Roblox feature set.
Switching to new Instance(className[, parent]) solves all of these problems and simplifies the compiler design.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:6
- Comments:9 (8 by maintainers)
Top Results From Across the Web
Replace Constructor with Factory Method refactoring - JetBrains
Right-click and choose Refactor | Replace Constructor with Factory Method from the context menu. Choose ReSharper | Refactor | Convert | ...
Read more >Refactoring to Patterns: Creation | Replace Constructors with ...
Joshua Kerievsky presents six refactorings that target design problems in everything from constructors to overly complicated construction ...
Read more >Is there a way to refactor these constructors? - Stack Overflow
Here are the constructors to my object private static class myObject { public myObject(int argA) { this.argA = argA; } public myObject(int ...
Read more >Replace Constructors with Creation Methods - Industrial Logic
Constructors on a class make it hard to decide ... Replace the constructors with intention-revealing. Creation Methods that return object instances.
Read more >invalid argument - Refactoring Away from Static Constructors
Refactoring Away from Static Constructors ... One of the great barriers to making code testable is the idea of the static constructor. This...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found

I disagree with these changes.
While this is a rather contrived example, this pattern does have its use cases. Writing reusuable code is a cornerstone of well-designed software, and falling back to the
new Instance("ClassName")pattern breaks the simplicity of this pattern, forcing us to implement a special case for Roblox Instances.Instantiating one class shouldn’t give you a different one. In TypeScript, it’s very strange and unconventional that instantiating one class would give you another (
e.g. new Instance("Part")gives you aPart, but you instantiated anInstance). This might seem commonplace coming from Lua, but this is not at all common practice and potentially confusing elsewhere. This case is essentially a flag parameter – which are best avoided.The current pattern is standard TypeScript and gracefully integrates with the language. Roblox’s Instance system is painfully unconventional when compared to the other instantiable objects in the API (such as datatypes). When writing TypeScript, it seems obvious to me that class-like objects should act like TypeScript classes. I do not agree with the idea that we should break the patterns of this language feature found in nearly every TypeScript program just to align slightly better with the way Roblox implemented Instances.
We can consider another aspect of roblox-ts: arrays. We have a crazy amount of methods and fields on arrays that aren’t present at all in Lua. It definitely benefits us as developers to augment arrays with methods for quality of life and to more closely resemble standard TypeScript. I think the same reasoning is applicable in this situation as well. While the pattern of using classes to represent Roblox Instances is not a direct 1:1 relationship with Lua, I strongly believe that it benefits us as TypeScript developers to do so. Not only do we get to write more conventional code, but we get to do so without needing to worry about special cases for Roblox Instances (think easy, polymorphic interoperability between your own classes and Instances – not possible after these changes!) or making any concessions.
Sure, we might have to add an extra paragraph in the docs, but that seems like a small price to pay for the benefits we gain.
An example of existing code that will be irrevocably broken without hacks or a much more complex solution is my
rbx-resourceslibrary. Right now, it can accept a class value and instantiate that for you or return it if it already exists:But it also works with custom objects that you have saved in the game already, but obviously these custom types cannot be automatically instantiated if they are missing:
The problem, if these proposed changes are implemented, is that without being able to use
RemoteEventdirectly as a value, the code can have no way to know if that object can be instantiated or not, as we’d be forced to resort to strings. The best solution would just be to wrapnew Instance(className)in a try/catch and see if it works – ew.This is just one example of something that becomes much harder to do if these changes become reality – and many similar polymorphic use cases will fall into the same bucket.
Counter-Points
Namespace Collisions TypeScript code conventionally uses camelCase for variables, and PascalCase for classes. All of the current Instance types follow this converntion, so users should not find naming collisions with regular variables. But even if they are making custom classes, they should not make a class that shares the same name as a Roblox instance in the first place, because this breaks the type system – regardless of if the proposed changes are implemented or not, because classes always becomes types as well.
Should users be allowed to deviate from this casing convention? Of course, but there is always going to be a penalty for being unconventional. Conventions do exist for a reason–compatibility, organization, and intuitiveness to name a few–so users should know what they’re getting into by going against the grain.
Extending Classes While this is a caveat to using TypeScript classes to represent Roblox Instances and datatypes, it’s probably the only prominent one. I think the benefits we gain from having a standard and conventional class system far outweigh this point. We can just throw a TranspilerError for this, and in the future add it to our custom TSLint rules. But I don’t really think this is even an big issue to begin with, because it isn’t likely someone would accidentally bump into this.
Using Instance Classes as Values I believe that this is a good thing! Right now we dynamically generate “class values” for instances to match other Roblox data types – a table with a
newfield that creates that type of Instance. I see this as a good thing, as we are normalizing the Roblox API and it opens up a world of new opportunities, notably myrbx-resourcesmodule as mentioned above.Learning Curve I don’t think making concessions like this because it makes coming from Lua easier is a good reason. Users of roblox-ts should learn how to use TypeScript and adopt its conventions rather than bringing baggage from Lua along. There are already a lot of things that are bad practice in TypeScript that users regularly do in Lua, so it’s not like this is the only mentality shift that will need to occur when switching to TypeScript. In fact, this is probably one of the least obtrusive ones.
It’s worth noting that users should expect us to have breaking changes before v0.1. We’re still very much in the “design” phase. But of course, if we do introduce a change like this, it would be announced via Discord.