Scripting in Needle Engine
Differences, similarities and key concepts of Typescript, Javascript and C#.
Last updated
Differences, similarities and key concepts of Typescript, Javascript and C#.
Last updated
The following guide tries to highlight some of the key differences between C#, Javascript and Typescript. This is most useful for developers new to the web ecosystem.
Here are also some useful resources for learning how to write Typescript:
CSharp or C# is a statically typed & compiled language. It means that before your code can run (or be executed) it has to be compiled - translated - into IL or CIL, an intermediate language that is a little closer to machine code. The important bit to understand here is that your code is analyzed and has to pass certain checks and rules that are enforced by the compiler. You will get compiler errors in Unity and your application not even start running if you write code that violates any of the rules of the C# language. You will not be able to enter Play-Mode with compiler errors.
Javascript on the other hand is interpreted at runtime. That means you can write code that is not valid and cause errors - but you will not see those errors until your program runs or tries to execute exactly that line that has the error. For example you can write var points = 100; points += "hello world";
and nobody will complain until you run the code in a browser.
Typescript is a language designed by Microsoft that compiles to javascript It adds a lot of features like for example type-safety. That means when you write code in Typescript you can declare types and hence get errors at compile-time when you try to e.g. make invalid assignments or call methods with unexpected types. Read more about types in Javascript and Typescript below.
Vanilla Javascript does (as of today) not have any concept of types: there is no guarantuee that a variable that you declared as let points = 100
will still be a number later in your application. That means that in Javascript it is perfectly valid code to assign points = new Vector3(100, 0, 0);
later in your code. Or even points = null
or points = myRandomObject
- you get the idea. This is all OK while you write the code but it may crash horrible when your code is executed because later you write points -= 1
and now you get errors in the browser when your application is already running.
As mentioned above Typescript was created to help fix that problem by adding syntax for defining types.
It is important to understand that you basically still write Javascript when you write Typescript and while it is possible to circumvent all type checking and safety checks by e.g. adding //@ts-ignore
above a erroneous line or defining all types as any
this is definitely not recommneded. Types are here to help you find errors before they actually happen. You really dont want to deploy your website to your server only to later get reports from users or visitors telling you your app crashed while it was running.
While vanilla Javascript does not offer types you can still add type-annotations to your javascript variables, classes and methods by using .
In C# you write variables either by using the type or the var
keyword.
For example you can either write int points = 100;
or alternatively use var
and let the compiler figure out the correct type for you: var points = 100
In Javascript or Typescript you have two modern options to declaring a variable.
For a variable that you plan to re-assign use let
, for example let points = 100;
For a variable that you do not want to be able to re-assign use const
, for example const points = 100;
Please note that you can still assign values to variables declared with const if they are (for example) a custom type. Consider the following example:
The above is perfectly fine Typescript code because you don't re-assign myPosition
but only the x
member of myPosition
. On the other hand the following example would not be allowed and cause a runtime or typescript error:
In Unity you usually add using
statements at the top of you code to import specific namespaces from Assemblies that are references in your project or - in certain cases - you migth find yourself importing a specific type with a name from a namespace.
See the following example:
This is how you do the same in Typescript to import specific types from a package:
You can also import all the types from a specific package by giving it a name which you might see here and there:
Vector2, Vector3, Vector4... If you have a C# background you might be familiar with the difference between a class and a struct. While a class is a reference type a struct is a custom value type. Meaning it is, depending on the context, allocated on the stack and when being passed to a method by default a copy is created. Consider the following example in C#:
A method is called with a Vector3 named position. Inside the method the passed in vector position
is modified: x is set to 42. But in C# the original vector that is being passed into this method (see line 2) is not changed and x will still be 0 (line 4).
The same is not true for Javascript/Typescript. Here we don't have custom value types, meaning if you come across a Vector in Needle Engine or three.js you will always have a reference type. Consider the following example in typescript:
Do you see the difference? Because vectors and all custom objects are in fact reference types we will have modified the original position
variable (line 3) and x is now 42.
This is not only important to understand for methods but also when working with variables. In C# the following code will produce two instances of Vector3 and changing one will not affect the other:
If you do the same in Typescript you will not create a copy but get a reference to the same myVector
instance instead:
While in C# you can use operator overloading this is not available in Javascript unfortunately. This means that while you can multiply a Vector3 in C# like this:
you have to use a method on the Vector3 type to archieve the same result (just with a little more boilerplate code)
loose vs strict comparison
In C# when you want to check if two variables are the same you can write it as follows:
in Javascript/Typescript there is a difference between ==
and ===
where ===
is more strictly checking for the type:
this
When you subscribe to an Event in C# you do it like this:
In Typescript and Javascript when you add a method to a list you have to "bind this". That essentially means you create a method where you explictly set this
to (usually) your current class instance. There are two way to archieve this.
Please note that we are using the type EventList
here which is a Needle Engine type to declare events (the EventList will also automatically be converted to a UnityEvent and or a event list in Blender when you use them with our Editor integrations)
There is also the more verbose "classical" way to archieve the same thing by manually binding this (and saving the method in a variable to later remove it again from the event list):
Be aware of var
You might come across the var
keyword in javascript as well but it is not recommended to use it and the modern replacement for it is let
. Learn more about .
You notice that the second variable playerIsNullOrUndefined
is using ==
which does a loose equality check in which case null
and undefined
will both result in true
here. You can read more about that
The short and recommended syntax for doing this is to use .