Je veux juste déclarer un Propriété statique dans le langage courant interface ? Je n'ai rien trouvé à ce sujet.
interface myInterface {
static Name:string;
}
Est-ce possible ?
Je veux juste déclarer un Propriété statique dans le langage courant interface ? Je n'ai rien trouvé à ce sujet.
interface myInterface {
static Name:string;
}
Est-ce possible ?
Retourne un type d'instance de I
et s'assure C
étend I
:
type StaticImplements<I extends new (...args: any[]) => any, C extends I> = InstanceType<I>;
Interface avec méthode d'instance :
interface MyInstance {
instanceMethod();
}
Interface avec méthode statique :
interface MyClassStatic {
new (...args: any[]): MyInstance;
staticMethod();
}
Classe nécessitant une méthode statique et s'étendant avec sa propre méthode :
class MyClass implements StaticImplements<MyClassStatic, typeof MyClass> {
static staticMethod();
static ownStaticMethod();
instanceMethod();
ownInstanceMethod();
}
La définition de méthodes statiques dans les interfaces est abordée dans le document #33892 et les méthodes statiques abstraites sont abordées dans #34516 .
En se basant sur les réponses de Val et d'Aleksey (merci), cette solution :
En l'état - Lien vers le terrain de jeux :
MyClass.staticMethod(); // OK
MyClass.ownStaticMethod(); // OK
new MyClass().instanceMethod(); // OK
new MyClass().ownInstanceMethod(); // OK
Si vous voulez enlever staticMethod
de MyClass
- Lien vers le terrain de jeux :
class MyClass implements StaticImplements<MyClassStatic, typeof MyClass> {} // Type 'typeof MyClass' does not satisfy the constraint 'MyClassStatic'. Property 'staticMethod' is missing in type 'typeof MyClass' but required in type 'MyClassStatic'.
Si vous voulez enlever instanceMethod
de MyClass
- Lien vers le terrain de jeux :
class MyClass implements StaticImplements<MyClassStatic, typeof MyClass> {} // Class 'MyClass' incorrectly implements interface 'MyInstance'. Property 'instanceMethod' is missing in type 'MyClass' but required in type 'MyInstance'.
Une autre option non mentionnée ici consiste à définir une variable avec un type représentant l'interface statique et à lui assigner une expression de classe :
interface MyType {
instanceMethod(): void;
}
interface MyTypeStatic {
new(): MyType;
staticMethod(): void;
}
// ok
const MyTypeClass: MyTypeStatic = class MyTypeClass {
public static staticMethod() { }
instanceMethod() { }
}
// error: 'instanceMethod' is missing
const MyTypeClass1: MyTypeStatic = class MyTypeClass {
public static staticMethod() { }
}
// error: 'staticMethod' is missing
const MyTypeClass2: MyTypeStatic = class MyTypeClass {
instanceMethod() { }
}
L'effet est le même que dans réponse avec des décorateurs mais sans la surcharge des décorateurs
Pertinent suggestion/discussion sur GitHub
Je suis un peu surpris de voir à quel point les premières réponses sont trop compliquées ! Mais c'est peut-être simplement parce que ce fil de discussion est si vieux.
Edit : En fait, ma première tentative s'est avérée pratiquement inutile après quelques tests, et ce problème s'est avéré un peu plus difficile à résoudre que je ne l'avais initialement prévu.
Cependant, après environ une heure de bricolage, je pense avoir trouvé la solution la plus propre et la meilleure jusqu'à présent (en me basant sur mon idée initiale) ! Si la question posée est "Comment inclure des propriétés statiques dans une interface ?" alors je pense que c'est une réponse assez décente. C'est au moins mieux que d'étendre une classe si tout ce dont vous avez besoin est une interface (typage/exigences/restrictions compilées). Il n'y a pas vraiment d'inconvénient (enfin, peut-être un petit) à cela non plus puisque la solution est 100% ambiante (contrairement à extends
-comme certaines réponses le suggèrent) et les classes sont des constantes (des références immuables qui ne sont pas hissées lorsque l'on utilise la syntaxe standard de déclaration de classe au lieu d'une expression de classe comme je le fais ici). Cela n'entraîne pas de surcharge d'exécution et ne nécessite pas d'héritage de classe d'exécution. Vous pouvez définir la classe entière (les membres statiques et non statiques) dans une seule interface (classe ambiante utilisée comme une) !
Voici comment on fait !
/** ./interface.ts */
// In a file module (best), or wrap in ts module or namespace block
// Putting this in a module provides encapsulation ensuring that no one is
// at risk of misusing this class (it must be used as a type only).
// Export only a type reference which will make it error is someone tries
// to use it as a value (such as in an `extends` clause, or trying to
// instantiate it).
/**
* Other Ideas For Names To Differentiate From Actual Classes/Non-Ambient Values:
* MyClassInterface or _Interface_MyClass or MyClass_Interface or Interface_MyClass
**/
declare class _MyClassInterface {
static staticProp: string;
static staticMethod(): number;
readonly prop: boolean
/**
* Note: Above, readonly won't need to be specified in the real class
* but the prop *will* still be readonly anyway.
*
* Now for the only caveat!
* It does appear however that you cannot mark anything private or
* protected in this pseudo-interface which is a bummer, only props
* and methods that appear only in the real class can be.
*/
prop2: boolean;
method(): Function;
constructor(p1: string, p2: number);
}
export type MyClassInterface = typeof _MyClassInterface;
maintenant pour consommer l'interface
/** ./consumer.ts */
import { MyClassInterface } from "./interface" // type import
const MyClass: MyClassInterface = class {
static staticProp: string;
prop: boolean;
prop2: boolean;
protected onlyOnRealClass: boolean; /* this is ok since this prop doesn't exist on the interface */
static staticMethod() {
return 5;
}
method() {
return () => {};
}
constructor(p1: string, p2: number) {}
};
Notez que le typeof
est absolument essentiel ici (si je me souviens bien, c'est parce que sans lui, Typescript pense que nous spécifions le type de l'instance alors que ce que nous voulons vraiment est le type de la classe elle-même). Par exemple, lorsque nous faisons
const a: MyClass = new MyClass()
sans le site typeof
mot-clé, nous disons que a
devrait être un exemple de MyClass, et non MyClass elle-même.
abstract
s'assure que vous n'essayez pas accidentellement d'instancier la classe...
Edit : En fait, j'enlève le mot-clé abstract de ma réponse parce qu'il s'avère que la classe réelle hérite en fait de la propriété d'être abstraite de la classe ambiante (c'est logique), et ne sera donc pas instanciée sans que le compilateur ne se plaigne si la classe ambiante qui fournit son type est marquée abstract... il faudra juste faire en sorte que ts ne fasse pas d'erreur si la classe ambiante est accidentellement instanciée. Ce serait donc une bonne idée de préfixer la déclaration/le nom de la classe ambiante par un trait de soulignement et/ou d'inclure le mot Interface
dans le nom afin que son utilisation correcte soit claire (edit : J'ai depuis abordé ce problème en encapsulant l'interface dans un module de fichier, la rendant ainsi privée de tout autre code, puis en exportant seulement une référence de type vers elle).
et c'est tout ce qu'il y a à faire !
L'intégration de l'interface dans un module n'est pas totalement nécessaire, mais elle offre quelques petits avantages, dont les suivants :
l'annotation de type "publique" largement utilisée dans tout le code d'implémentation devient légèrement plus petite puisqu'elle n'inclut plus le mot-clé typeof
à la différence de la déclaration de classe/interface ambiante enveloppée, l'identifiant exporté/exposé est strictement un type (alias), de sorte qu'une erreur se produira désormais si quelqu'un essaie de l'instancier ou de l'utiliser dans une clause extends (ou de l'utiliser partout ailleurs où une valeur d'exécution est attendue)
Je ne fournis pas de nom de classe pour l'expression de classe dans cet exemple parce que les expressions de classe, comme toutes les expressions de fonction, héritent simplement de l'identifiant qui leur est attribué si un nom de classe n'est pas fourni. Ainsi, si votre identificateur est identique au nom que vous souhaitez donner à cette classe ou à cette fonction, vous pouvez le laisser de côté. Ou bien, vous pouvez en fournir un en ligne comme d'habitude et il aura la priorité sur l'identifiant. Le nom d'une classe ou d'une fonction peut également être modifié après la création de la fonction/classe, mais uniquement via Object.defineProperty ou Object.defineProperties.
Pour info, les cours peuvent en fait être implemented
(du moins dans les versions récentes de TS) par une autre classe, mais les propriétés statiques seront de toute façon ignorées. Il semble que implementing
tout ce qui s'applique uniquement à la prototype
dans les deux sens (vers/depuis).
Si vous cherchez à définir une classe statique (c'est-à-dire que toutes les méthodes/propriétés sont statiques), vous pouvez faire quelque chose comme ceci :
interface MyStaticClassInterface {
foo():string;
}
var myStaticClass:MyStaticClassInterface = {
foo() {
return 'bar';
}
};
Dans ce cas, la "classe" statique n'est en fait qu'un simple objet JS, qui implémente toutes les méthodes de MyStaticClassInterface
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.