Typescript: Tidak dapat memperluas tipe bawaan

Dibuat pada 14 Nov 2014  ·  14Komentar  ·  Sumber: microsoft/TypeScript

ES6 menunjuk beberapa objek asli sebagai subclassable, misalnya Object, Boolean, Error, Map, Promise ..etc. Cara konstruksi ini dimodelkan di lib.d.ts membuat tidak mungkin untuk subkelas, karena mereka didefinisikan sebagai pasangan var dan antarmuka.

Dari bagian spesifikasi ES6 19.5.1:

Konstruktor Error dirancang untuk menjadi subclassable. Ini dapat digunakan sebagai nilai klausa extends dari deklarasi kelas. Konstruktor subclass yang dimaksudkan untuk mewarisi perilaku Error yang ditentukan harus menyertakan panggilan super ke konstruktor Error untuk menginisialisasi instance subclass.

Saat ini, hal ini akan mengakibatkan kesalahan "Kelas A hanya dapat memperluas kelas lain":

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

Komentar yang paling membantu

Bahkan ada kemampuan untuk memperpanjang kelas Error , saya masih mengalami ketidaknyamanan dengan itu pada node. Misalnya saya mengalami masalah dengan "error.stack". Ketika saya melakukan throw new Error(...) saya mendapatkan error.stack , tetapi ketika saya melakukan throw new CustomError() - Saya tidak. Untuk mengatasinya saya terpaksa menggunakan trik ini:

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;
    }
}

Semua 14 komentar

Ini adalah masalah umum untuk setiap pustaka js yang mengekspos fungsi yang dimaksudkan untuk menjadi subclassable. Jika perpustakaan saya mengekspor konstruktor Foo dan mengharapkan untuk menerima contoh subkelas Foo (bukan hanya objek yang mengimplementasikan antarmuka mirip Foo), saat ini tidak ada cara untuk mengizinkannya.

Saya ingin bisa mengatakannya

class Error;

Tidak akan ada javascript yang dipancarkan untuk pernyataan seperti itu, hanya pembaruan tabel simbol Typecript.

Untuk kasus umum, saya ingin mengganti isi deklarasi kelas apa pun dengan titik koma, memungkinkan hal-hal seperti

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

yang berarti bahwa pustaka sudah menjamin bahwa Foo.prototype adalah turunan dari Bar.prototype dan mengimplementasikan antarmuka Baz.

@metaweta Saya pikir ini mungkin membuat beberapa redundansi:

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?

Ini masih memiliki beberapa potensi karena antarmuka subclassable akan bekerja sebagai kelas terbuka. # 819

@metaweta Kasus pertama Anda harus ditangani oleh deklarasi kelas ambien. misalnya

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

Untuk kasus kedua, saya akan mengajukan masalah saran yang berbeda untuk itu.

Berikut adalah daftar lengkap objek "subclassable" dari spesifikasi ES6. saat ini semuanya didefinisikan sebagai pasangan antarmuka / var.

  • Obyek
  • Boolean
  • Kesalahan
  • NativeError
  • Jumlah
  • Tanggal
  • Tali
  • Ekspresi Reguler
  • Himpunan
  • TypedArray (Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array, dan DataView)
  • Peta
  • Set
  • WeakMap
  • WeakSet
  • ArrayBuffer
  • DataView
  • Janji

Masalah:

  • Setelah suatu tipe didefinisikan sebagai kelas, tidak ada cara untuk memperpanjang sisi anggota (memperpanjang sisi statis dapat terjadi melalui modul).
  • Ekstensi yang ada di sisi anggota tidak akan berfungsi karena Anda tidak dapat menentukan ulang kelas (melanggar perubahan)
  • Tidak ada cara untuk memodelkan pemanggilan fungsi pada sebuah kelas, misalnya Object, String, Boolean, Date, Number, RegExp, Error, dan Array. (lagi-lagi melanggar perubahan)

Solusi yang memungkinkan:

  • Izinkan memperluas tipe apa pun dengan properti prototipe dan tanda tangan konstruksi
  • Izinkan perluasan sisi instance kelas (mis. Modul Class.prototype {}, dengan izin @RyanCavanaugh )
  • Izinkan menentukan tanda tangan panggilan pada konstruktor kelas

@mhegazy Terima kasih, saya tidak menyadari konstruksi deklarasi kelas ambient.

Ketika saya memperluas kelas Error dengan cara ini (menggunakan definisi kelas ambient), melempar ExtendedError tidak menghasilkan pelacakan tumpukan atau menetapkan pesan dengan benar.

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 ini sepertinya masalah mesin. sesuai spesifikasi ES6 itu harus berfungsi. Mesin harus memperbaikinya sekarang setelah ES6 diratifikasi.

Saya berhasil membuatnya bekerja. Dalam kode js saya yang berfungsi, saya memiliki panggilan Error.captureStackTrace tetapi saya mengeluarkannya ketika saya menerapkannya di ts karena deklarasi Error tidak memilikinya.

Ada contoh kode:
Ini adalah Pengecualian C # yang membutuhkan 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;
        }
    }
}

Diperbaiki oleh # 3516. Kelas sekarang dapat memperluas ekspresi arbitrer dari tipe fungsi konstruktor.

Rilis TypeScript apa yang diperbaiki ini akan dikirimkan?
Saya dengan cepat memeriksa label rilis untuk 1.5.3 dan 1.5.4, dan sepertinya itu belum dikirim, hanya di master untuk saat ini.

EDIT:
Maaf, saat ini kami telah memperhatikan bahwa bug yang ditandai dengan pencapaian "TypeScript 1.6"

Terima kasih atas pekerjaan Anda!

@kostrse Anda dapat mencoba rilis nighty TypeScript 1.6 kami dengan menjalankan npm install -g typescript@next .

Hai,

Saya menggunakan Typecript 1.6.2 dan lib.es6.d.ts saya menunjukkan Error (dan Array, ...) sebagai Antarmuka bukan kelas.
Apakah ini sudah diperbaiki di 1.6.2?

Bersulang!

@jpsfs perbaikannya, seperti dicatat oleh @ahejlsberg di https://github.com/Microsoft/TypeScript/issues/1168#issuecomment -112955503 adalah mengizinkan kelas untuk memperluas ekspresi sembarang jenis fungsi konstruktor.

Bahkan ada kemampuan untuk memperpanjang kelas Error , saya masih mengalami ketidaknyamanan dengan itu pada node. Misalnya saya mengalami masalah dengan "error.stack". Ketika saya melakukan throw new Error(...) saya mendapatkan error.stack , tetapi ketika saya melakukan throw new CustomError() - Saya tidak. Untuk mengatasinya saya terpaksa menggunakan trik ini:

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;
    }
}
Apakah halaman ini membantu?
0 / 5 - 0 peringkat