Typescript: Izinkan visibilitas pada konstruktor

Dibuat pada 13 Mar 2015  ·  42Komentar  ·  Sumber: microsoft/TypeScript

Saya pikir itu adalah pola yang cukup umum untuk memiliki metode pabrik statis untuk membuat kelas dan konstruktor kelas ini bersifat pribadi sehingga Anda tidak dapat membuat instance kelas kecuali jika Anda menggunakan metode pabrik.

Fixed Suggestion help wanted

Komentar yang paling membantu

Nah, hal yang sama berlaku untuk fungsi pribadi di kelas, bukan? Saya tidak mengerti mengapa kami tidak bisa mendapatkan kesalahan kompilasi untuk mengakses konstruktor pribadi.

Semua 42 komentar

Karena semua "kelas" sebenarnya hanya fungsi dan karena tidak ada fungsi yang tidak dapat dipanggil, di mana pun kelas terlihat, Anda juga dapat menggunakan operator new di atasnya.

Namun Anda dapat membuat kelas menjadi non-publik (misalnya dalam modul) dan mengekspor antarmuka untuk kelas tersebut. Ini menyalahgunakan fakta bahwa antarmuka dapat memperluas kelas tetapi mereka tidak dapat langsung dipakai atau diperluas.

Nah, hal yang sama berlaku untuk fungsi pribadi di kelas, bukan? Saya tidak mengerti mengapa kami tidak bisa mendapatkan kesalahan kompilasi untuk mengakses konstruktor pribadi.

@billccn Saya tidak suka menghentikan permintaan dengan "JS memungkinkan maka Anda tidak akan dapat menyembunyikan".
Satu hal terlindungi sepenuhnya di TS & JS yang dihasilkan, hal lainnya adalah melindungi itu sampai ke titik menghasilkan JS. Seperti yang telah Anda jelaskan, perlindungan penuh tidak mungkin, tetapi memiliki visibilitas berbeda yang diperiksa oleh kompilator, seharusnya memungkinkan.
Jika Anda tidak menyukai pengubah visibilitas, gunakan publik default di mana-mana, ada hal lain yang menganggap konsep itu berguna.

Ya, saya kira jika bidang pribadi diimplementasikan sebagai pemeriksaan kompilator saja, maka itu mungkin dapat diperluas ke konstruktor. Namun, solusi berbasis antarmuka sudah berfungsi.

: +1: Ada kalanya saya ingin memaksa programmer untuk menggunakan metode pabrik agar kode dapat dibaca. Solusi berbasis antarmuka menciptakan banyak gangguan dalam kode.

Saya pikir pemeriksaan kompiler saja adalah cara untuk pergi.

Diterima, menerima PR

Untuk memperjelas, dapatkah kelas dengan konstruktor privat dapat diperpanjang?

yaitu, Apakah ini akan menimbulkan kesalahan?

class A {
    private constructor() {
    }
}

class B extends A { // Should there be an error at A saying: "Cannot extend private class 'A'"?
}

jika demikian, apakah kami akan mengizinkan ini:

class A {
    protected constructor(a?: any)
    private constructor() {

    }
}

class B extends A { // No error since 'A' has a non-private constructor
}

Dari pengalaman pengembang non-JS, itulah perilaku yang diharapkan.

Dalam contoh pertama, B seharusnya error karena panggilan super implisitnya ilegal. Jadi kelas dengan private constructor secara efektif sealed / final .

Dalam contoh kedua, A deklarasi seharusnya error karena semua kelebihan konstruktor, dan implementasinya, harus memiliki visibilitas yang sama (aturan yang sama yang kita miliki untuk metode).

Lihat juga # 471. Apakah benar-benar diperlukan untuk mengizinkan konstruktor menjadi pribadi atau akan dilindungi?

@benliddicott terkadang berguna untuk memaksa single atau memaksa programmer menggunakan salah satu metode statis untuk membuat objek, karena terkadang itu bisa lebih mudah dibaca.

Lihat disini .

@dsherret protected memenuhi semua kebutuhan itu.

Namun Anda tidak dapat memastikan bahwa pengguna hilir tidak akan pernah memiliki kebutuhan yang sah untuk mewarisi dari kelas Anda. Efek satu-satunya dari private adalah mencegah pengguna hilir Anda memenuhi kebutuhan yang tidak Anda antisipasi.

@benliddicott Terkadang satu-satunya hal yang Anda inginkan adalah agar kelas tidak dapat diperpanjang. Saya merujuk Anda ke Item Java Efektif 15 Meminimalkan Mutabilitas, terutama:

"2. Pastikan bahwa kelas tidak dapat diperpanjang. Hal ini mencegah subkelas yang ceroboh atau jahat dari membahayakan perilaku kelas yang tidak dapat diubah dengan berperilaku seolah-olah status objek telah berubah. Mencegah subkelas umumnya dilakukan dengan membuat kelas menjadi final, tetapi ada adalah alternatif yang akan kita bahas nanti.

Saat ini tidak ada final / sealed dukungan di TypeScript, jadi konstruktor pribadi adalah satu-satunya cara untuk mencapai kelas yang tidak dapat diubah dari perspektif sistem tipe. (Padahal, saya merekomendasikan orang juga membekukan objek di konstruktor.)

@billccn , pendapat penulis itu menarik. Begitu pula ide bahwa pendapat penulis perpustakaan harus mengesampingkan pendapat pengguna perpustakaan. Pengalaman saya sendiri adalah bahwa penulis perpustakaan tidak tahu kapan harus menggunakan pribadi, dan menggunakannya secara berlebihan, menyebabkan sakit kepala bagi pengguna, hanya karena mereka yakin mereka tahu bagaimana perpustakaan mereka akan digunakan, padahal sebenarnya tidak.

Tetapi daripada bahasa statis seperti Java, perbandingan yang lebih tepat adalah Perl, bahasa dinamis lainnya: http://www.perlmonks.org/?node_id=437623

Salah satu alasan mengapa perl tidak memiliki pengubah akses seperti publik, pribadi, dan dilindungi, adalah karena disadari bahwa mereka sering menghalangi pekerjaan: apa yang dibayangkan oleh perancang asli tidak ada hubungannya dengan apa yang ingin Anda lakukan dengannya. Dengan cara yang sama, desain untuk fleksibilitas - meskipun Anda mungkin tidak melihat diri Anda membutuhkannya sekarang, orang berikutnya yang akan datang mungkin melihatnya sangat berguna untuk memecahkan masalah baru itu, dan akan memberkati kejeniusan Anda karena mengembangkan fleksibilitas itu ;-)

dan:

Perl tidak tergila-gila dengan privasi yang diberlakukan. Akan lebih suka jika Anda tidak masuk ke ruang tamunya karena Anda tidak diundang, bukan karena memiliki senapan

http://www.perlmonks.org/?node_id=1096925

Sebenarnya JavaScript itu sama, dan - ya - TypeScript sama, hampir dalam segala hal. Dalam skrip ketikan Anda dapat mengakses anggota pribadi dengan baik - menggunakan escape-hatch yang diberi nama tepat: obj["propertyName"] .

Jika sebagai penulis perpustakaan Anda mencurigai tidak bijaksana untuk memanggil metode atau mewarisi dari suatu objek, maka beri tahu pengguna itu tidak bijaksana. Tapi jangan cegah mereka - mereka mungkin lebih tahu dari Anda.

Saya tidak mengerti diskusi tentang "dilindungi menjadi cukup". Jika TS memiliki konsep visibilty dan saya dapat menerapkan konsep ini pada konstruktor, jawabannya adalah "it is not enough".

Jika pengubah akses diizinkan pada konstruktor maka menurut saya itu harus memiliki perilaku yang konsisten dengan pengubah lain dan mengizinkan pribadi.

Anggota pribadi pada umumnya pasti berguna. Mereka memungkinkan Anda untuk mengatur dan memfaktorkan ulang detail implementasi kelas tanpa khawatir menyebabkan efek samping di luar kelas.

Dengan konstruktor pribadi, saya mungkin ingin memaksa pengembang di tim saya untuk memprogram dengan cara tertentu. Misalnya, saya mungkin ingin memaksa mereka untuk menggunakan metode statis di sini karena lebih mudah dibaca dan memaksa mereka untuk tidak memperluas kelas ini:

class Currency {
    private constructor(private value: number, private type: CurrencyType) {
    }

    static fromNumberAndType(value: number, type: CurrencyType) {
        return new Currency(value, type);
    }

    static fromString(str: string) {
        const value = ...,
              type  = ...;

        return new Currency(value, type);
    }

    // ... omitted ...
}

// error:
const badCurrency = new Currency(5.66, CurrencyType.USD);
// ok:
const goodCurrency1 = Currency.fromNumberAndType(5.66, CurrencyType.USD);
const goodCurrency2 = Currency.fromString("5.66 USD");

Dengan konstruktor pribadi, saya mungkin ingin memaksa pengembang di tim saya untuk memprogram dengan cara tertentu.

Itu masalah manajemen, bukan masalah desain bahasa.

@benliddicott Hal serupa dapat Anda katakan tentang deskriptor tipe pada variabel :) Jika Anda tidak menyukai fiturnya, gunakan saja JS biasa. ATAU
Gunakan TS dan buat beberapa aturan mirip lint yang melarang penggunaan private pada konstruktor. Untuk memparafrasekan komentar terakhir Anda: "Itu masalah perkakas, bukan masalah desain bahasa".

@benliddicott jika ada sesuatu yang tidak mungkin dilakukan maka saya tidak perlu mengirimkannya kembali ketika ada kesalahan setelah melakukan peninjauan kode. Itu menghemat waktu.

Memberi tahu compiler dengan tepat bagaimana kode harus digunakan dengan benar adalah aset yang memberikan umpan balik yang tepat kepada pengembang yang menggunakannya saat mereka memprogram.

@dsherret Tidak, ini adalah batasan sewenang-wenang yang memberdayakan Architecture astronauts : -1:

@jbondc Apakah

@jbondc Saya tidak percaya istilah "astronot arsitektur" relevan di sini. Bukankah itu berbicara tentang orang yang menghabiskan lebih banyak waktu untuk memikirkan arsitektur daripada menulis kode ? Keputusan untuk menggunakan konstruktor pribadi bisa cepat dan sederhana seperti menggunakan hampir semua fitur dalam bahasa seperti TypeScript.

Selain itu, menurut saya ini tidak "sewenang-wenang" karena dapat membantu mencegah penyalahgunaan kode. Mungkin saya ingin mengingatkan diri saya sendiri atau tim saya untuk tidak menggunakan konstruktor dan sebaliknya menggunakan salah satu metode statis atau memaksa penggunaan metode statis untuk menegakkan singleton. Ini cepat dan sederhana dan saya mendapatkan umpan balik yang tepat dari kompiler. Jika itu sewenang-wenang maka Anda dapat berargumen bahwa banyak aspek bahasa yang sewenang-wenang. Mungkin Anda memiliki lebih banyak untuk dikatakan tentang kesewenang-wenangan yang tidak Anda ungkapkan dalam komentar Anda?

Ini adalah fitur bahasa yang jika orang tidak suka, mudah bagi mereka untuk tidak menggunakannya.

@dsherret Tidak cukup untuk mendokumentasikan alih-alih memaksakan batasan lain?

Ini memperumit banyak warisan secara signifikan, lihat # 4805 (yang bagi saya sepertinya hanya renungan sekarang). Saya sudah mengungkapkan beberapa pemikiran saya di # 3578 jadi tidak perlu repot melakukannya lagi. Keluar sedikit kuat dengan astronaut , tidak bermaksud menyinggung siapa pun.

Kelas @jbondc dengan konstruktor pribadi tidak akan bisa diturunkan. Mereka pada dasarnya disegel dan warisan, apalagi warisan ganda, tidak boleh bekerja dengan ini.

Jauh lebih baik memiliki kode yang mendokumentasikan diri sendiri daripada menuliskannya secara eksternal atau dalam komentar. Itulah salah satu alasan saya menyukai semua batasan TypeScript dan pada dasarnya apa yang kami minta dalam fitur ini — hanya cara lain untuk mendokumentasikan kode dengan menggunakan bahasa itu sendiri.

Nah, inilah cara lain untuk menulis contoh Anda:

module Currency {
    export enum Type {
        CAD = 1.3,
        USD = 1,
    }

    class PrivateCurrency {
        constructor(private value: number, private type: Type) { }
    }

    export function fromNumberAndType(value: number, type: Type) {
        return new PrivateCurrency(value, type);
    }

    export function fromString(str: string) {
        let value = 10;
        let type = Type.CAD;
        return new PrivateCurrency (value, type);
    }
}

Kurang OO-ish tetapi Anda sebenarnya memiliki kelas pribadi yang 'benar-benar' nyata.

@jbondc Senang sekali Anda menemukan jalan untuk kelas privat! Biarkan orang lain menggunakan pendekatan lain (kelas yang dapat diekspor dengan konstruktor pribadi). Sekarang Anda dapat mengamati bahwa ada fitur yang memenuhi kebutuhan kelompok A dan tidak mengganggu kerja kelompok B. :)

+1

+1

Apakah ini masih di radar? PR sangat basi!

+1

+1

+1

: +1: dapat berguna untuk pola desain pabrik

Mohon maaf jika ini adalah pengulangan atau hanya terlambat ke pesta, tetapi kami menggunakan pola berikut untuk mencapai konstruktor pribadi:

interface ExampleBuilder {
    Instance(): Example;
}

export interface Example {
    Val(): number;
}

export let Example: ExampleBuilder = class ExampleImpl {
    constructor(v: number) {
    }

    Val(): number {
        return 42;
    }

    static Instance(): Example {
        return new ExampleImpl(2);
    }
};

let x = Example.Instance(); // OK: x has type Example
let y = new Example(5);     // ERROR: Cannot use 'new' with an expression whose type lacks a call or construct signature.

Perhatikan bahwa ini dapat lebih dirapikan lagi menggunakan pengubah readonly baru saja digabungkan.

@myitcv cukup keren untuk memberlakukannya untuk saat ini ... mungkin cara terbaik yang disebutkan menurut pendapat saya. Ini masih terlalu bertele-tele dan membutuhkan beberapa detik untuk memahami apa yang terjadi, itulah sebabnya pengubah akses masih akan sangat baik pada konstruktor.

Secara pribadi, saya hanya menandai semua konstruktor pribadi masa depan saya dengan // todo: make private once supported komentar dan tidak memanggil konstruktor. Alangkah baiknya setelah fitur ini hadir untuk mendapatkan penegakan hukum dan dokumentasi yang lebih baik dengan pengubah akses.

@detikfood

Ini masih terlalu bertele-tele dan membutuhkan beberapa detik untuk memahami apa yang sedang terjadi

Sepakat. Kami tidak terlalu menderita dari beban kognitif itu karena kelas-kelas ini dihasilkan oleh kode. Jadi antarmuka ke programmer sebenarnya bagus dan sederhana.

diperbaiki oleh https://github.com/Microsoft/TypeScript/pull/6885

terima kasih @AbubakerB!

Hai, apakah fitur ini akan dirilis di beberapa jenis skrip versi mendatang?
Sampai sekarang, mencoba mendeklarasikan konstruktor privat atau dilindungi memberi saya kesalahan ini dalam skrip tipe 1.8.10:
error TS1089: pengubah 'private' tidak dapat muncul di deklarasi konstruktor.

Ahh, sudahlah. Baru mengetahui peta jalan yang menyatakan fitur ini akan dimasukkan dalam skrip ketikan 2.0.

Ahh, sudahlah. Baru mengetahui peta jalan yang menyatakan fitur ini akan dimasukkan dalam skrip ketikan 2.0.

Juga pencapaian ditetapkan ke TypeScript 2.0 dan label Fixed akan menunjukkan bahwa itu disertakan. Apa pun dengan label Fixed biasanya disertakan dalam master dan tersedia melalui npm install typescript@next .

Saya menggunakan TypeScript 2.0.2 RC dan masih mendapatkan TS1089 ketika saya mencoba membuat konstruktor private . Apakah saya salah melakukannya atau ini tidak diperbaiki?

Saya menggunakan TypeScript 2.0.2 RC dan masih mendapatkan TS1089 ketika saya mencoba membuat konstruktor pribadi. Apakah saya salah melakukannya atau ini tidak diperbaiki?

itu bekerja untuk saya. pastikan alias perintah Anda mengarah ke versi yang benar, dan editor Anda diperbarui untuk menggunakan versi TS terbaru.

Saya menemukan masalahnya. Itu adalah kesalahan gulp-typescript yang menggunakan versi yang salah dari tsc meskipun spesifikasi saya di package.json dan apa yang diselesaikan pada PATH .

Bagi orang lain yang mengalami masalah ini, solusi saya adalah mengedit gulpfile.js dan ...

  1. require TypeScript sebelum gulp-typescript sebagai berikut:
// Tool-Chain: Scripts
var tsc = require("typescript");
var typescript = require('gulp-typescript');
  1. Sediakan kompiler sebagai pengganti saat menentukan tugas build saya:
// Task(s): Build TypeScript Outputs
var tsconfig = typescript.createProject("path to tsconfig", { typescript: tsc });
gulp.task('build:scripts', function () {
    let ts = tsconfig.src()
                     .pipe(sourcemaps.init())
                     .pipe(typescript(tsconfig));

    return ts.js.pipe(sourcemaps.write(".")).pipe(gulp.dest(path.join(outputs.root, outputs.scripts)))
});
Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

Zlatkovsky picture Zlatkovsky  ·  3Komentar

jbondc picture jbondc  ·  3Komentar

DanielRosenwasser picture DanielRosenwasser  ·  3Komentar

wmaurer picture wmaurer  ·  3Komentar

blendsdk picture blendsdk  ·  3Komentar