En C #, il existe une différence énorme entre les interfaces et les classes. En effet, une classe représente un type de référence, de sorte que nous pouvons réellement créer des objets modélisés sur cette classe, tandis que les interfaces sont censées être des contrats auxquels une classe signe pour garantir l'existence d'un comportement donné. En particulier, nous ne pouvons pas créer d'instances d'interfaces.
Le problème avec les interfaces est d'exposer le comportement. Une classe l'implémente en donnant une implémentation explicite dudit comportement.
Dans ce cas, bien que les interfaces puissent contiennent des propriétés, nous nous soucions le plus souvent des interfaces en raison de problèmes de comportement. La plupart des interfaces sont donc des contrats de comportement.
Sur TypeScript, par contre, il me semble que quelque chose me met mal à l'aise et, en vérité, je l'ai vu plus d'une fois, ce qui explique la raison de cette question.
Dans un tutoriel, j'ai vu ceci:
export interface User {
name: string; // required with minimum 5 chracters
address?: {
street?: string; // required
postcode?: string;
}
}
Mais attendez une minute. Pourquoi User
est une interface? Si nous pensons comme C #, User
ne devrait pas être une interface. En vérité, il semblerait que nous définissions le type de données User
au lieu d’un contrat de comportement.
En pensant comme nous le faisons en C #, la chose naturelle serait la suivante:
export class User {
public name: string;
public address: Address;
}
export class Address {
public street: string;
public postcode: string;
}
Mais utiliser des interfaces, comme nous le faisons avec des classes, pour définir simplement un type de données, plutôt que de définir un contrat de comportement, semble très courant en TypeScript.
Alors à quelles interfaces sont destinés dans TypeScript? Pourquoi les gens utilisent-ils des interfaces dans TypeScript comme nous utilisons des classeurs en C #? Comment les interfaces doivent être correctement utilisées dans TypeScript: établir des contrats de comportement, ou définir des propriétés et des objets doivent avoir?
Considérons qu'en Javascript, les données sont souvent échangées sous forme d'objets simples, souvent via JSON:
let data = JSON.parse(someString);
Disons que cette data
est un tableau d'objets User
, et nous le passerons à une fonction:
data.forEach(user => foo(user))
foo
serait tapé comme ceci:
function foo(user: User) { ... }
Mais attendez, nous n’avons jamais fait new User
! Devrions nous? Devrions-nous écrire une classe User
et map
tout le data
, même si le résultat serait exactement le même, un Object
avec des propriétés? Non, ce serait de la folie juste pour satisfaire le système de types, mais ne changerait rien au runtime. Un simple interface
qui décrit comment (se "comporter") l'objet spécifique est parfaitement suffisant ici.
Je suis également venu à TypeScript à partir d'un arrière-plan C # et je me suis demandé les mêmes choses. Je pensais dans le sens des POCO (POTO est-il une chose?)
Alors à quelles interfaces sont destinés dans TypeScript?
Le manuel TypeScript semble indiquer que les interfaces sont conçues pour "définir des contrats dans votre code".
Pourquoi les gens utilisent-ils des interfaces dans TypeScript comme nous utilisons des classes en C #?
Je suis d'accord avec la réponse de @ deceze ici.
John Papa développe le sujet des classes et des interfaces sur son blog . Il suggère que les classes sont les mieux adaptées pour "créer plusieurs nouvelles instances, en utilisant des objets d'héritage, et singleton". Ainsi, selon l'intention des interfaces TypeScript telles que décrites dans le manuel TypeScript et selon l'opinion d'un homme, il semblerait que les classes ne sont pas nécessaires pour établir des contrats dans TypeScript. Au lieu de cela, vous devriez utiliser des interfaces. (Vos sens C # seront toujours offensés.)
Les interfaces doivent être correctement utilisées dans TypeScript: pour établir des contrats de comportement, ou pour définir des propriétés et des objets doivent avoir?
Si je comprends la question, vous demandez si les interfaces doivent établir contrats de comportement ou contrats de structure. A cela, je répondrais: les deux. Les interfaces TypeScript peuvent toujours être utilisées de la même manière que les interfaces en C # ou en Java (c'est-à-dire pour décrire le comportement d'une classe), mais elles offrent également la possibilité de décrire la structure des données.
De plus, mon collègue m'a incité à utiliser des classes au lieu d'interfaces, car celles-ci ne produisent aucun code dans le compilateur.
Exemple:
Ce TypeScript:
class Car implements ICar {
foo: string;
bar(): void {
}
}
interface ICar {
foo: string;
bar(): void;
}
produit ce javascript:
var Car = (function () {
function Car() {
}
Car.prototype.bar = function () {
};
return Car;
}());
Les interfaces dans TypeScript sont similaires aux interfaces en C # en ce qu'elles fournissent toutes les deux un contrat. Cependant opposés aux interfaces C # qui ne contiennent que des méthodes, les interfaces TypeScript peuvent également décrire des champs ou des propriétés que les objets contiennent. Par conséquent, ils peuvent également être utilisés pour des tâches qui ne sont pas directement possibles avec les interfaces C #.
Une différence majeure entre les interfaces et les classes dans TypeScript est que les interfaces n’ont pas de représentation au moment de l’exécution et qu’aucun code n’est émis pour elles. Les interfaces sont très largement utilisables. Par exemple, vous pouvez utiliser des littéraux d'objet pour construire des objets avec une interface satisfaisante. Comme:
let user: User = {
name: 'abc',
address: {
street: 'xyz',
},
};
Ou vous pouvez affecter n'importe quel objet de données (par exemple, reçu via l'analyse JSON) à une interface (mais vos vérifications préalables doivent affirmer qu'il s'agit de données vraiment valides). Par conséquent, les interfaces sont très flexibles pour les données.
D'autre part, les classes ont un type associé au moment de l'exécution et un code est généré. Vous pouvez vérifier le type au moment de l'exécution avec instanceof
et une chaîne de prototypes a été configurée. Si vous définissez User
en tant que classe, ce ne sera pas un utilisateur valide, sauf si vous appelez la fonction constructeur. Et vous ne pouvez pas simplement définir un type de données approprié comme étant un User
. Vous devez créer une nouvelle instance et copier les propriétés sur.
Ma règle personnelle:
Comment les interfaces doivent être correctement utilisées dans TypeScript: établir des contrats de comportement, ou définir des propriétés et des objets doivent avoir?
Les interfaces dans TypeScript sont des contrats de forme, décrivant la structure attendue d'un objet. Si une valeur a une certaine annotation d'interface, vous vous attendez à ce qu'il s'agisse d'un objet contenant les membres définis dans l'interface. Les membres peuvent être des valeurs ou des fonctions (méthodes). Généralement, leur comportement (corps de fonction) ne fait pas partie du contrat. Mais vous pouvez spécifier si elles sont readonly
ou non.
Alors à quelles interfaces sont destinés dans TypeScript? Pourquoi les gens utilisent-ils des interfaces dans TypeScript comme nous utilisons des classeurs en C #?
Les interfaces TypeScript peuvent jouer le même rôle que les interfaces C # si elles sont supposées être implémentées par les classes TypeScript.
Mais non seulement une classe peut implémenter une interface; tout type de valeur peut:
interface HelloPrinter {
printHello(): void
}
L'objet suivant n'est pas une classe mais implémente néanmoins l'interface:
{
printHello: () => console.log("hello")
}
Ainsi on peut faire
const o: HelloPrinter = {
printHello: () => console.log("hello")
}
et le compilateur TypeScript ne se plaindra pas.
L'objet implémente notre interface sans nous obliger à écrire une classe.
Travailler avec des interfaces est plus léger que de travailler avec des classes (interfaces et).
Toutefois, si vous devez connaître le nom du type (nom de classe/d’interface) lors de l’exécution, les classes sont le bon choix, car les noms d’interface ne sont connus que lors de la compilation.
En utilisant uniquement le mécanisme de désérialisation natif, vous ne pouvez pas désérialiser une instance d'une classe spécifique. Vous pouvez uniquement désérialiser dans un objet plain-old-javascript. De tels objets peuvent adhérer aux interfaces TypeScript mais ne peuvent pas être une instance d'une classe. Si vous devez gérer des données qui traversent une limite de sérialisation, telles que les données attendues d'un service Web, utilisez des interfaces. Si vous devez générer vous-même de nouvelles occurrences de telles valeurs, construisez-les littéralement ou créez une fonction pratique qui les retourne - des objets adhérant à cette interface.
Une classe peut elle-même implémenter une interface, mais cela peut prêter à confusion si vous envisagez de traiter à la fois des instances de classe construites localement ET des objets non standard reconstitués et désérialisés. Vous ne pourrez jamais vous fier à la base de classe de l'objet et il n'y aurait donc aucun avantage à le définir également en tant que classe dans ce but précis.
J'ai réussi à créer un module ServerProxy responsable de l'envoi de code d'un service Web - appel du service Web et résultat renvoyé. Si vous vous associez à des modèles knock-out ou similaires, vous pouvez avoir une classe qui encapsule le modèle lié à l'interface utilisateur avec un constructeur qui sait comment lever un objet plain-old-javascript renvoyé qui adhère au contrat d'interface uniquement du service Web. une instance de votre classe de modèle.