Comparatif : de C# à JavaScript / TypeScript

Ce guide présente une comparaison de la syntaxe entre C# et JavaScript (ES6/ES2015 minimum) / TypeScript.

Vous trouverez d’autres guides et une explication plus générale ici.

Pourquoi TypeScript ? En tant que développeur C#, vous allez adorer, et on vous explique pourquoi ici. Ce guide précise clairement quand une fonctionnalité est propre à TypeScript. Tout le reste est du JavaScript standard.

Déclarations des données

En JavaScript, les variables peuvent être déclarées dans l’espace global. Depuis l’ES6, utilisez uniquement le mot-clé let.

var myData = "csharp";
view raw variable.cs hosted with ❤ by GitHub

let myData = 'js';
view raw variable.js hosted with ❤ by GitHub

L’ES6 a introduit les constantes en JavaScript. Contrairement à C#, des valeurs complexes peuvent être utilisées.

const string myData = "csharp";
readonly User myUser = new User();
view raw constant.cs hosted with ❤ by GitHub

const MY_DATA = 'js';
const MY_USER = new User();
view raw constant.js hosted with ❤ by GitHub

Types de données simples

Contrairement à C#, le typage des données est dynamique en JavaScript natif (une variable peut changer de type à tout moment), et vous n’avez pas besoin de le préciser (les types sont inférés automatiquement). TypeScript ajoute un typage statique et explicite. Tous les nombres partagent le même type en JavaScript.

bool userMan = true;
int userAge = 81;
float userAverage = 10.5;
string userName = "Henri Bergson";
view raw types.cs hosted with ❤ by GitHub

let userMan: boolean = true;
let userAge: number = 81;
let userAverage: number = 10.5;
let userName: string = 'Henri Bergson';
view raw types.ts hosted with ❤ by GitHub

En plus de null, il existe en JavaScript le type undefined, et NaN (Not a Number). Ce sont des erreurs, ne les utilisez pas.

Détails des chaînes de caractères

Contrairement à C#, vous pouvez utilisez indifféremment les simples et doubles guillements pour les chaînes de caractères en JavaScript. L’ES6 a introduit les template literals : de nouveaux guillemets, les backticks, pour l’interpolation des variables et dans lesquels les sauts de ligne sont autorisés. Evitez la concaténation classique en JavaScript, cela porte à confusion avec les additions et peut aboutir à des erreurs de types.

string userFullName = userFirstName + " " + userLastName;

let userFullName: string = `${userFirstName} ${userLastName}`;

C’est aussi pratique pour gérer les guillements simples et doubles dans une même chaîne.

let HTMLTemplate: string = `<p class="content">My name's Henri !</p>`;

Listes de données

Contrairement à C#, la taille des tableaux est toujours dynamique en JavaScript, vous pouvez donc ajouter des éléments à tout moment.

string[] userBooks = {"Book 1", "Book 2"};
userBooks[0];
userBooks.Length;
view raw array.cs hosted with ❤ by GitHub

let userBooks: string[] = [`Book 1`, `Book 2`];
userBooks.push(`Book 3`);
userBooks[0];
userBooks.length;
view raw array.ts hosted with ❤ by GitHub

Les dictionnaires C# sont appelés des objets en JavaScript, avec des index de type string.

Dictionary<string, string> user = new Dictionary<string, string>();
user.Add("firstName", "Henri");
user.Add("lastName", "Bergson");
user["firstName"];
view raw dictionnary.cs hosted with ❤ by GitHub

let user = {
firstName: `Henri`,
lastName: `Bergson`
};
user.firstName;
view raw object.js hosted with ❤ by GitHub

Comme les objets JavaScript sont comme des instances littérales, TypeScript vous permet d’utiliser les interfaces pour typer les objets.

interface User {
age: number;
name: {
first: string,
last: string
};
}
let user: User = {
age: 81,
name: {
first: `Henri`,
last: `Bergson`
}
};
view raw interface.ts hosted with ❤ by GitHub

L’ES6 a introduit de nouveaux types de collections : MapSetWeakMapWeakSet.

Décomposition

L’ES6 permet de déstructurer les tableaux, c’est-à-dire extraire les valeurs dans différentes variables. Cela n’existe pas sous cette forme en C#.

let myList: number[] = [1, 2, 3];
let [data1, data2, data3] = myList;

Lié à cette façon de faire, l’opérateur spread permet de diffuser les valeurs d’un tableau en plusieurs valeurs successives. Cela n’existe pas sous cette forme en C#.

let myOptions: int[] = [1, 2];
function test(option1: int, option2: int): void {}
test(myOptions);
let defaultConfig: string[] = ['data1', 'data2'];
let userConfig: string[] = ['data3', 'data4'];
let finalConfig: string[] = [
defaultConfig,
userConfig
];
view raw spread.ts hosted with ❤ by GitHub

On peut aussi décomposer des objets de façon similaire. La version 7 de C# introduit aussi un concept de destructuring mais sous une autre forme.

let user = {
firstName: `Henri`,
lastName: `Bergson`
};
let { firstName: myName } = user;
myName; // `Henri`

Souvent, vous voudrez créer une variable portant le même nom que la propriété. Auquel cas vous pouvez raccourcir à :

let user = {
firstName: `Henri`,
lastName: `Bergson`
};
let { firstName } = user;
firstName; // `Henri`

Et si la propriété n’existe pas dans l’objet ? Vous pouvez prévoir une valeur par défaut.

let user = {
firstName: `Henri`,
lastName: `Bergson`
};
let { firstName = `default`, age = 0 } = user;
firstName; // `Henri`
age; // 0

Blocs

Même syntaxe pour les conditions en C# et en JavaScript.

Depuis l’ES6, grâce à let, la portée des variables est limitée au bloc en cours. Contrairement à C#, une variable d’un bloc enfant peut surcharger une variable d’un bloc parent en JavaScript.

for (int i = 0; i < 10; i++) {}
view raw loop.cs hosted with ❤ by GitHub

for (var i: number = 0; i < 10; i++) {}
i; // 10, error prone
for (let i: number = 0; i < 10; i++) {}
i; // undefined
view raw loop.ts hosted with ❤ by GitHub

Les itérations sont simplifiées en ES6. N’utilisez pas for ... in en JavaScript, ce n’est pas fait pour les tableaux.

for (var value in userBooks) {}
view raw iteration.cs hosted with ❤ by GitHub

for (let value of userBooks) {}
view raw iteration.js hosted with ❤ by GitHub

Une autre possibilité pour les itérations complexes (avec les valeurs et les index, seulement pour les tableaux).

for (int i = 0; i < books.Count; i++) {
books[i];
}
view raw iteration.cs hosted with ❤ by GitHub

userBooks.forEach((value, index) => {});
view raw iteration.js hosted with ❤ by GitHub

Fonctions

En JavaScript, vous avez accès aux portées parentes directement.

let myData: string = 'js';
function myMethod(): void {
myData; // 'js'
}
view raw scope.ts hosted with ❤ by GitHub

Contrairement à C#, pas de surcharge en JavaScript (une fonction ne peut avoir qu’une seule signature), et les paramètres sont toujours facultatifs. L’ES6 a introduit les valeurs par défaut. TypeScript permet de rendre automatiquement les paramètres sans valeur par défaut obligatoires (c’est une différence majeure avec du JavaScript natif).

void myMethod(string required, string optional = "default") {}
view raw params.cs hosted with ❤ by GitHub

function myMethod(required: string, optional: string = 'default'): void {}
view raw params.ts hosted with ❤ by GitHub

Vous pouvez aussi avoir un nombre indéfini de paramètres.

void function myArrayPush(params int[] values) {
for (int i = 0; i < values.Length; i++) {
}
myArrayPush(1, 2, 3);
view raw rest.cs hosted with ❤ by GitHub

function myArrayPush(values: number[]): void {
for (let value of values) {}
}
myArrayPush(1, 2, 3);
view raw rest.ts hosted with ❤ by GitHub

Arrow functions

L’ES6 a introduit une syntaxe raccourcie pour les fonctions anonymes, appelée arrow functions, équivalente aux expressions lambda en C#. A utiliser systématiquement en JavaScript, car cela règle un problème spécifique de contexte.

numbersList.Where(value => value > 2);
view raw arrow.cs hosted with ❤ by GitHub

numbersList.filter((value) => value > 2);
view raw arrow.js hosted with ❤ by GitHub

Méthodes natives

Quelques unes des méthodes de base.

myEmail.IndexOf("@");
myEmail.Replace("@", " at ");
myEmail.Substring(0, 5);
myEmail.Length;
Int32.Parse("5");
view raw methods.cs hosted with ❤ by GitHub

myEmail.strpos('@');
myEmail.replace('@', ' at ');
myEmail.substring(0, 5);
myEmail.length;
Number.parseInt('5', 10);
view raw methods.js hosted with ❤ by GitHub

Quand une fonction semble être appelée directement, comme setTimeout(), c’est parce que l’objet global est implicite : window.setTimeout().

Classes

L’ES6 a introduit la syntaxe des classes, pour simplifier la programmation orienté objet en JavaScript. Les propriétés sont déclarées directement dans le constructeur. Les propriétés pré-déclarées et les modificateurs de visibilité apparaîtront dans une prochaine version de JavaScript, mais TypeScript permet de les utiliser dès maintenant.

public class User {
public string FirstName;
public User(string firstName) {
FirstName = firstName;
}
public void SayHello() {}
}
User myUser = new User("Henri");
myUser.FirstName;
myUser.SayHello();
view raw class.cs hosted with ❤ by GitHub

class User {
public firstName: string; // TypeScript only
public constructor(firstName: string) {
this.firstName = firstName;
}
public sayHello(): void {}
}
let myUser: User = new User(`Henri`);
myUser.firstName;
myUser.sayHello();
view raw class.ts hosted with ❤ by GitHub

Lors d’un héritage en JavaScript, l’appel au constructeur parent est obligatoire dans le constructeur fils, et, contrairement à C#, vous pouvez surcharger des méthodes sans mot-clé spécifique.

public class Editor: User {
public Editor(string firstName) {
base(firstName);
}
public override void SayHello() {
base.SayHello();
}
}
view raw inheritance.cs hosted with ❤ by GitHub

class Editor extends User {
public constructor(firstName: string) {
super(firstName);
}
public sayHello(): void {
super.sayHello();
}
}
view raw inheritance.ts hosted with ❤ by GitHub

Comme en C#, les getters et setters ont une syntaxe spéciale en JavaScript.

public class User {
private string name;
public string Name {
get {
return name;
}
set {
name = value;
}
}
}
User myUser = new User("Henri");
myUser.Name;
myUser.Name = "New name";
view raw accessors.cs hosted with ❤ by GitHub

class User {
protected _name: string;
public get name(): string {
return this._name;
}
public set name(newName: string) {
this._name = newName;
}
}
let myUser: User = new User(`Henri);
myUser.name;
myUser.name = `New name`;
view raw accessors.ts hosted with ❤ by GitHub

La syntaxe est la même pour les méthodes statiques en C# et JavaScript.

class Utilities {
static filter(): void {}
}
Utilities.filter();
view raw static.ts hosted with ❤ by GitHub

TypeScript ajoute les classes abstraites et les interfaces.

interface Movable {
void Move();
}
public class Vehicle: Movable {
public void Move() {}
}
public abstract class Test {}
view raw interface.cs hosted with ❤ by GitHub

interface Movable {
move(): void;
}
class Vehicle implements Movable {
public move(): void {}
}
abstract class Test {}
view raw interface.ts hosted with ❤ by GitHub

Namespaces

L’encapsulation est gérée directement par le système de chargement depuis l’ES6 : s’il y a l’instruction export ou l’instruction import, alors vous n’êtes plus en portée globale. Soyez vigilants : l’extension’ .js est nécessaire dans les imports JavaScript, mais en TypeScript, comme vous devrez gérer à la fois des fichiers .ts en développement et des fichiers .js en production, il est préférable de configurer le système de chargement pour ajouter automatiquement la bonne extension.

// User.cs
namespace Accounts
{
public class User {}
}
// script.cs
using Accounts;
User myUser = new User();
view raw namespace.cs hosted with ❤ by GitHub

// module.js
export class User {}
// script.js
import { User } from './module.js';
let myUser = new User();
view raw namespace.js hosted with ❤ by GitHub

Revenir au sommaire des guides JavaScript