C # is the best substitute for TypeScript?

C # is the best substitute for TypeScript?

TypeScript is excellent. It is a perfect combination of strong typing and rapid development, so it is very easy to use. I will choose this library by default in many cases. However, there is no perfect language in the world, and TypeScript is not the most appropriate tool in some cases:

  • Performance critical (e.g., real-time communications, video games)
  • You need to interact with native code, such as C/C + + or Rust
  • A more stringent type system (e.g. financial system) is required

In these cases, TypeScript developers are better off using other languages. C #, Go, and Java are great choices. They are much faster than TypeScript, and each language has its own strengths. C # can work well with TypeScript. Let me explain why.

 

 

 

TypeScript is JavaScript with C#

C # works well with TypeScript because they look like the same language. Both were designed by Anders Hejlsberg, and in many ways TypeScript is JavaScript with c#. Their features and syntax are similar, so it's easy to combine them in the same project. More importantly, the language of c# is very similar to TypeScript, so it is very easy for developers to read and write code. Instead, Go is a completely different language: no classes, no inheritance, no exceptions, There is no package level encapsulation (only class level encapsulation), and the syntax is completely different. Of course, this is not necessarily a bad thing, but developers do need to rethink and design code in different ways. Therefore, it is difficult to use Go and TypeScript at the same time. However, Java is very similar to c# but still lacks many functions of c# and TypeScript.

 

Similarities between C# and TypeScript

As you may already know, C # and TypeScript have many similarities, such as C-based syntax, classes, interfaces, generics, etc. Next, let me list the similarities between the two in detail:

  • async/await
  • lambda expressions and functional array methods
  • Operators (?,!,?) for handling null
  • deconstruction
  • Command line interface (CLI)

async/await

First, both c# and JavaScript use async/await to handle asynchronous code. In JavaScript, asynchronous operations are represented by promise, and applications can await an asynchronous operation. Promise in C # is actually a Task, which is identical to promise in concept and has corresponding methods. The following example demonstrates the use of async/await in two languages:

1 async function fetchAndWriteToFile(url: string, filePath:string): Promise<string> {
2   // fetch() returns aPromise
3   const response = awaitfetch(url);
4   const text = awaitresponse.text();
5   // By the way, we'reusing Deno (https://deno.land)
6   awaitDeno.writeTextFile(filePath, text);
7   return text;
8 }

 

Example of async/await in TypeScript

 1 using System.IO;
 2 using System.Net.Http;
 3 using System.Threading.Tasks;
 4 
 5 async Task<string> FetchAndWriteToFile(string url, stringfilePath) {
 6   // HttpClient.GetAsync()returns a Task
 7   var response = await newHttpClient().GetAsync(url);
 8   var text = awaitresponse.Content.ReadAsStringAsync();
 9   awaitFile.WriteAllTextAsync(filePath, text);
10   return text;
11 }

 

An example of async/await in C #

The following is the Promise API of JavaScript and the equivalent C# Task API:

JavaScript API

Equivalent C# API

Promise.all()

Task.WaitAll()

Promise.resolve()

Task.FromResult()

Promise.reject()

Task.FromException()

Promise.prototype.then()

Task.ContinueWith()

new Promise()

new TaskCompletionSource()

Lambda expressions and functional array methods

Both C # and JavaScript use the familiar = > syntax (i.e. arrow function) to represent lambda expressions. The following is a comparison between TypeScript and C #:

1 const months = ['January', 'February', 'March', 'April'];
2 const shortMonthNames = months.filter(month => month.length< 6);
3 const monthAbbreviations = months.map(month =>month.substr(0, 3));
4 const monthStartingWithF = months.find(month => {
5   returnmonth.startsWith('F');
6 });

 

lambda expressions used in TypeScript

1 using System.Collections.Generic;
2 using System.Linq;
3 
4 var months = new List<string> {"January","February", "March", "April"};
5 var shortMonthNames = months.Where(month => month.Length <6);
6 var monthAbbreviations = months.Select(month =>month.Substring(0, 3));
7 var monthStartingWithF = months.Find(month => {
8   returnmonth.StartsWith("F");
9 });

 

Using lambda expressions in C #

The above example demonstrates the system of C# Some methods in LINQ namespace are equivalent to JavaScript functional array methods. The following is the array method of JavaScript and the equivalent C# Linq method:

JavaScript API

Equivalent C# API

Array.prototype.filter()

Enumerable.Where()

Array.prototype.map()

Enumerable.Select()

Array.prototype.reduce()

Enumerable.Aggregate()

Array.prototype.every()

Enumerable.All()

Array.prototype.find()

List.Find()

Array.prototype.findIndex()

List.FindIndex()

Handle null operators

The same is true for C# and TypeScript's handling of nulls:

Feature nameSyntaxDocumentation links
Optional properties property? TS : https://www.typescriptlang.org/docs/handbook/2/objects.html#optional-properties

C#: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-reference-types

Non-null assertion object!.property

TS: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator

C#: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-forgiving

Optional chaining object?.property

JS : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining

C#: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-

Nullish coalescing object ?? alternativeValue JS: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator

C#: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator

deconstruction

Although C# does not support array or class deconstruction by default, it supports Tuple and Record deconstruction. Users can also define deconstruction for custom types. The following are examples of deconstruction in TypeScript and C#:

const author = { firstName: 'Kurt', lastName: 'Vonnegut' };
// Destructuring an object:
const { firstName, lastName } = author;

const cityAndCountry = ['Indianapolis', 'United States'];
// Destructuring an array:
const [city, country] = cityAndCountry;

 

Examples of deconstruction in TypeScript

 1 using System;
 2 
 3 var author = new Author("Kurt", "Vonnegut");
 4 // Deconstructing a record:
 5 var (firstName, lastName) = author;
 6 
 7 var cityAndCountry = Tuple.Create("Indianapolis","United States");
 8 // Deconstructing a tuple:
 9 var (city, country) = cityAndCountry;
10 
11 // Define the Author record used above
12 record Author(string FirstName, string LastName);

 

Examples of deconstruction in C #

Command line interface (CLI)

My development method is to write code using a text editor, then run commands on the terminal, build and run. For TypeScript, this means that you need to use node or deno command line interface (CLI). C# also has a similar cli called dotnet (named by c#'s. NET runtime). Here are some examples of using dotnet CLI:

 1 mkdir app && cd app
 2 # Create a new console application
 3 # List of available app templates:https://docs.microsoft.com/dotnet/core/tools/dotnet-new
 4 dotnet new console
 5 # Run the app
 6 dotnet run
 7 # Run tests (don't feel bad if you haven't written those)
 8 dotnet test
 9 # Build the app as a self-contained
10 # single file application for Linux.
11 dotnet publish -c Release -r linux-x64

 

 

Basic functions (classes, generics, errors, and enumerations)

These are the more basic similarities between TypeScript and C#. The following examples are about these aspects:

 1 import { v4 as uuidv4 } from'https://deno.land/std/uuid/mod.ts';
 2 
 3 enum AccountType {
 4   Trial,
 5   Basic,
 6   Pro
 7 }
 8 
 9 interface Account {
10   id: string;
11   type: AccountType;
12   name: string;
13 }
14 
15 interface Database<T> {
16   insert(item: T):Promise;
17   get(id: string):Promise<T>;
18 }
19 
20 class AccountManager {
21 
22   constructor(database:Database<Account>) {
23     this._database =database;
24   }
25 
26   asynccreateAccount(type: AccountType, name: string) {
27     try {
28       const account = {
29         id: uuidv4(),
30         type,
31         name;
32       };
33       awaitthis._database.insert(account);
34     } catch (error) {
35         console.error(`Anunexpected error occurred while creating an account. Name: ${name}, Error:${error}`);
36     }
37   }
38 
39   private _database:Database<Account>;
40 }

 

Examples of TypeScript classes

 1 using System;
 2 using System.Threading.Tasks;
 3 
 4 enum AccountType {
 5   Trial,
 6   Basic,
 7   Pro
 8 }
 9 
10 record Account(string Id, AccountType Type, string Name);
11 
12 interface IDatabase<T> {
13   Task Insert(T item);
14   Task<T> Get(stringid);
15 }
16 
17 class AccountManager {
18 
19   publicAccountManager(IDatabase<Account> database) {
20     _database = database;
21   }
22 
23   public async voidCreateAccount(AccountType type, string name) {
24     try {
25       var account = newAccount(
26        Guid.NewGuid().ToString(),
27         type,
28         name
29       );
30       await_database.Insert(account)
31     } catch (Exceptionexception) {
32      Console.WriteLine($"An unexpected error occurred while creating anaccount. Name: {name}, Exception: {exception}");
33     }
34   }
35 
36   IDatabase<Account>_database;
37 }

 

Examples of C# classes

Other advantages of C #

Similar to TypeScript is not the only advantage of C#, it has other advantages:

  • Easier to combine with native code
  • event
  • Other functions

Combined with native code

One of the biggest advantages of C # is that it can go deep into native code. As mentioned at the beginning of this article, TypeScript is not good at combining with C/C + + code. Node.js has a plug-in that supports native C/C + +, called node API, but it needs to write additional C + + wrappers for native functions to convert native types into JavaScript objects, or vice versa, similar to the way JNI works. C # can call native functions directly, just put the library in the bin directory of the application, and then define the API as an external function in C #. Then you can use external functions like c# functions NET runtime handles the conversion between c# data types and native data types. For example, if the native library exports the following C function:

1 int countOccurrencesOfCharacter(char *string, char character) {
2     int count = 0;
3     for (int i = 0;string[i] != '\0'; i++) {
4         if (string[i] ==character) {
5             count++;
6         }
7     }
8     return count;
9 }

 

It can be called from C# in the following way:

 1 using System;
 2 using System.Runtime.InteropServices;
 3 
 4 var count = MyLib.countOccurrencesOfCharacter("C# is prettyneat, eh?", 'e');
 5 // Prints "3"
 6 Console.WriteLine(count);
 7 
 8 class MyLib {
 9     // Just placeMyLibraryName.so in the app's bin folder
10    [DllImport("MyLibraryName")]
11     public static externint countOccurrencesOfCharacter(string str, char character);
12 }

 

This method can access any dynamic library (. so,. dll or. dylib) through C connection, that is, you can easily call code written in C, C + +, Rust, Go or other languages, as long as it is compiled into machine code. Other applications of native interaction include:

    • Pass pointer to native object as IntPtr
    • Use getfunctionpointorfordelegate() to pass the C# method as a function pointer to the native function
    • Use marshal Ptrtostringansi() converts a C string to a c# string
    • Convert structures and arrays

 

event

A unique feature of C # is that it provides first-class event support. In TypeScript, you can implement the addEventListener() method to let the client listen to events, and c# has the event keyword, which can be used to define events, The event is notified to all listeners through simple syntax (instead of manually traversing all event listeners like TypeScript and executing in the try/catch block). For example, we can let the Connection class define a MessageReceived event, as shown below:

1 class Connection {
2     // AnAction<string> is a callback that accepts a string parameter.
3     public eventAction<string> MessageReceived;
4 }

 

Using the Connection code, you can add a processing function to MessageReceived through the + = operator, as follows:

1 var connection = new Connection();
2 connection.MessageReceived += (message) => {
3    Console.WriteLine("Message was received: " + message);
4 };

 

The Connection class can call MessageReceived internally to trigger the MessageReceived event for all listeners:

1 // Raise the MessageReceived event
2 MessageReceived?.Invoke(message);

 

 

Other advantages

  • Performance: c# fast. C #'s ASP Net web framework has always been among the best in Techempower's evaluation, while C #'s NET CoreCLR runtime performance is improving with each major version. One of the reasons c# has good performance is that by using structures instead of classes, applications can minimize or even completely eliminate garbage collection. Therefore, c# is very popular in video game programming.
  • Games and mixed reality: C # is one of the most popular languages for game development. Game engines such as Unity, Godot and even Unreal use C #. C # is also popular in mixed reality because VR and AR applications are written in Unity.
  • Because C# has first-party libraries, tools and documentation, some tasks are very easy to implement. For example, it is much easier to create gRPC clients in C# than TypeScript. Instead, in node When using TypeScript in JS, you must find the correct combination of modules and tools to correctly generate JavaScript gRPC client and corresponding TypeScript types.
  • Advanced functions: C# has many functions that other languages do not have, such as operator overloading, destructors, etc.

summary

As mentioned earlier, there is no perfect language in the world. There are always trade-offs when designing languages, so some languages are faster, On the other hand, some languages are very easy to use, but it is usually more difficult to optimize performance (for example, the dynamic language feature of JavaScript). For this reason, I believe it will be very useful to master a group of similar languages: these languages have their own strengths, but they are very similar and can cooperate with each other. For example, here is a group of languages I choose:

TypeScript

  • The highest level language, the fastest development speed

  • Performance is not optimal, but it is suitable for most applications

  • Not suitable for combination with native code

C#

  • It is still a high-level language and supports garbage collection, so it is easy to use, although not as easy as TypeScript.

  • In terms of speed and memory usage, its performance is better than TypeScript

  • Most importantly, it can be well combined with the bottom layer

C++

  • Development is difficult (for example, manual memory management is required), so the development speed will be much slower

  • But runtime performance is the best! It can be used everywhere and can be combined with many existing software

  • Like C #, the standard library is very good, but there are many pitfalls (most of which are related to memory management). I prefer to use Rust because it has better memory security, but many of my work needs to be combined with the existing C + + code, so it will be easier to use C + +.

 

Keywords: C#

Added by phwae on Wed, 29 Dec 2021 04:44:17 +0200