Typescript: Impossible d'étendre les types intégrés

Créé le 14 nov. 2014  ·  14Commentaires  ·  Source: microsoft/TypeScript

L'ES6 désigne certains objets natifs comme sous-classables, par exemple Object, Boolean, Error, Map, Promise ..etc. La façon dont ces constructions sont modélisées dans lib.d.ts rend impossible la sous-classe, car elles sont définies comme une paire d'un var et d'une interface.

À partir de la section 19.5.1 des spécifications ES6:

Le constructeur Error est conçu pour être sous-classable. Il peut être utilisé comme valeur d'une clause extend d'une déclaration de classe. Les constructeurs de sous-classe qui ont l'intention d'hériter du comportement Error spécifié doivent inclure un super appel au constructeur Error pour initialiser les instances de sous-classe.

Actuellement, cela entraînerait l'erreur "Une classe ne peut étendre qu'une autre classe":

class NotImplementedError extends Error {
    constructor() {
        super("Not Implemented");
    }
}
Bug ES6 Fixed

Commentaire le plus utile

Même s'il est possible d'étendre la classe Error de nos jours, j'ai toujours des inconvénients avec cela sur le nœud. Par exemple, j'ai des problèmes avec "error.stack". Quand je fais throw new Error(...) j'obtiens error.stack , mais quand je fais throw new CustomError() - je ne le fais pas. Pour le résoudre, j'ai forcé d'utiliser cette astuce:

export class HttpError extends Error {
    httpCode: number;
    message: string;

    constructor(httpCode: number, message?: string) {
        super();
        if (httpCode)
            this.httpCode = httpCode;
        if (message)
            this.message = message;

        this.stack = new Error().stack;
    }
}

Tous les 14 commentaires

C'est un problème général pour toute bibliothèque js qui expose des fonctions censées être sous-classables. Si ma bibliothèque exporte le constructeur Foo et s'attend à recevoir des instances de sous-classes de Foo (pas seulement des objets implémentant une interface de type Foo), il n'y a actuellement aucun moyen de permettre cela.

Je voudrais pouvoir dire

class Error;

Il n'y aurait pas de javascript émis pour une telle déclaration, seulement une mise à jour de la table des symboles de Typescript.

Pour le cas général, j'aimerais pouvoir remplacer le corps de toute déclaration de classe par un point-virgule, permettant des choses comme

class Foo<X, Y> extends Bar<X> implements Baz<Y>;

ce qui signifierait que la bibliothèque garantit déjà que Foo.prototype est une instance de Bar.prototype et implémente l'interface Baz.

@metaweta Je pense que cela peut faire des redondances:

interface Foo {
}
class Foo; // Foo is now a class!
interface Bar extends Foo {
}
class Bar extends Foo; // Two `extends Foo`s. 

interface X {
}
class X extends Foo; // Hmm?
interface Y extends Foo {
}
class Y; // Hmm?

Il a encore des potentiels car les interfaces sous-classables fonctionneront comme des classes ouvertes. # 819

@metaweta votre premier cas doit être traité par une déclaration de classe ambiante. par exemple

// myLib.d.ts
declare class Error {
}
// no code emitted here for this. 
// myfile.ts
// Error is subclassable,
class MyError extends Error {
}

Pour le deuxième cas, je déposerais un problème de suggestion différent pour celui-ci.

Voici la liste complète des objets "sous-classables" de la spécification ES6. actuellement, ils sont tous définis comme une paire interface / var.

  • Objet
  • Booléen
  • Erreur
  • NativeError
  • Nombre
  • Date
  • Chaîne
  • RegExp
  • Tableau
  • TypedArray (Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array et DataView)
  • Carte
  • Ensemble
  • WeakMap
  • WeakSet
  • ArrayBuffer
  • DataView
  • Promettre

Problèmes:

  • Une fois qu'un type est défini en tant que classe, il n'y a aucun moyen d'étendre le côté membre (l'extension du côté statique peut se faire via un module).
  • Les extensions existantes côté membre ne fonctionneront pas car vous ne pouvez pas redéfinir une classe (changement de rupture)
  • Aucun moyen de modéliser les appels de fonction sur une classe, par exemple Object, String, Boolean, Date, Number, RegExp, Error et Array. (encore une fois changement de rupture)

Solutions possibles:

  • Autoriser l'extension de tout type avec une propriété prototype et une signature de construction
  • Autoriser l'extension du côté instance d'une classe (par exemple, module Class.prototype {}, avec l'aimable autorisation de @RyanCavanaugh )
  • Autoriser la définition des signatures d'appel sur un constructeur de classe

@mhegazy Merci, je n'étais pas au courant de la construction de la déclaration de classe ambiante.

Lorsque j'étends la classe Error de cette façon (en utilisant la définition de classe ambiante), lancer ExtendedError ne produit pas de trace de pile ni n'affecte correctement le message.

declare class Error {
  constructor(message?: string);
}

class ExtendedError extends Error {
  constructor(message?: string) {
    super(message + " extended");
  }
}

//throw new Error("Test");
throw new ExtendedError("Test");

@unional cela ressemble à un problème de moteur. selon les spécifications ES6, cela devrait fonctionner. Les moteurs devraient résoudre ces problèmes maintenant que l'ES6 a été ratifié.

J'ai réussi à le faire fonctionner. Dans mon code js qui fonctionnait, j'ai l'appel Error.captureStackTrace mais je le retire lorsque je l'implémente dans ts car la déclaration d'erreur ne l'a pas.

Il existe un exemple de code:
C'est une exception de type C # qui prend une exception innerException.

declare class Error {
    public name:string;
    public message:string;
    public stack:string;
    constructor(message?:string);
    static captureStackTrace(error: Error, constructorOpt: any);
}

class Exception extends Error {
    public innerException: Error;
    constructor(message?: string, innerException?: Error|string) {
        // Guard against throw Exception(...) usage.
        if (!(this instanceof Exception)) return new Exception(message, innerException);
        super();
        if (typeof Error.captureStackTrace === 'function') {
            //noinspection JSUnresolvedFunction
            Error.captureStackTrace(this, arguments.callee);
        }
        this.name = "Exception";
        if (innerException) {
            if (innerException instanceof Error) {
                this.innerException = innerException;
                this.message = message + ", innerException: " + this.innerException.message;
            }
            else if (typeof innerException === "string") {
                this.innerException = new Error(innerException);
                this.message = message + ", innerException: " + this.innerException.message;
            }
            else {
                this.innerException = innerException;
                this.message = message + ", innerException: " + this.innerException;
            }
        }
        else {
            this.message = message;
        }
    }
}

Corrigé par # 3516. Les classes peuvent désormais étendre des expressions arbitraires de types de fonction constructeur.

Quelle version de TypeScript ce correctif va-t-il être livré?
J'ai rapidement vérifié les étiquettes de version pour 1.5.3 et 1.5.4, et il semble qu'il n'a pas encore été expédié, uniquement en master pour le moment.

ÉDITER:
Désolé, en ce moment, nous avons remarqué que le bogue marqué par le jalon "TypeScript 1.6"

Merci pour votre travail!

@kostrse, vous pouvez essayer nos versions nocturnes de TypeScript 1.6 en exécutant npm install -g typescript@next .

Salut,

J'utilise Typescript 1.6.2 et mon lib.es6.d.ts affiche Erreur (et Array, ...) comme Interface not class.
Est-ce déjà corrigé dans 1.6.2?

À votre santé!

@jpsfs le correctif, comme indiqué par @ahejlsberg dans https://github.com/Microsoft/TypeScript/issues/1168#issuecomment -112955503 est de permettre aux classes d'étendre des expressions arbitraires de types de fonctions de constructeur.

Même s'il est possible d'étendre la classe Error de nos jours, j'ai toujours des inconvénients avec cela sur le nœud. Par exemple, j'ai des problèmes avec "error.stack". Quand je fais throw new Error(...) j'obtiens error.stack , mais quand je fais throw new CustomError() - je ne le fais pas. Pour le résoudre, j'ai forcé d'utiliser cette astuce:

export class HttpError extends Error {
    httpCode: number;
    message: string;

    constructor(httpCode: number, message?: string) {
        super();
        if (httpCode)
            this.httpCode = httpCode;
        if (message)
            this.message = message;

        this.stack = new Error().stack;
    }
}
Cette page vous a été utile?
0 / 5 - 0 notes