Akses anggota kelas luar di TypeScript

Sejak TypeScript 1.6, kita dapat dengan mudah membuat kelas dalam dengan ekspresi kelas. Dalam bahasa OOP-sentris lainnya seperti Java, kelas dalam dapat mengakses anggota kelas luar, bahkan yang pribadi.

Perilaku ini mirip dengan konsep penutupan, dimana fungsi dapat mengakses variabel dari ruang lingkup yang didefinisikan.

Mengapa saya tidak bisa mencapai ini di TypeScript? Apakah spesifikasi kelas di ECMAScript 2015 berperan di sini?

Kode yang menyajikan perilaku yang diharapkan:

class OuterClass {
    private outerField = 1337;

    public InnerClass = class { 
        public accessOuter() {
            return this.outerField; // outerField not defined
        }
    }
}

var outer = new OuterClass();
var inner = new outer.InnerClass();
var win = inner.accessOuter();

person Piotr Lewandowski    schedule 02.08.2016    source sumber
comment
lihat stackoverflow.com/a/45244695/279393 untuk mengetahui cara lain mencapai hal ini.   -  person bnieland    schedule 21.07.2017


Jawaban (4)


Lebih mudah untuk memahami mengapa Anda tidak dapat melakukan itu jika Anda melihat javascript kode Anda yang telah dikompilasi:

var OuterClass = (function () {
    function OuterClass() {
        this.outerField = 1337;
        this.InnerClass = (function () {
            function class_1() {
            }
            class_1.prototype.accessOuter = function () {
                return this.outerField; // outerField not defined
            };
            return class_1;
        }());
    }
    return OuterClass;
}());

Seperti yang Anda lihat, outerField didefinisikan sebagai anggota OuterClass seperti:

this.outerField = 1337;

Saat Anda mencoba mengaksesnya di InnerClass, Anda melakukan:

return this.outerField;

Namun this di sini adalah turunan dari class_1 dan bukan OuterClass jadi tidak ada outerField di this.
Selain itu, Anda tidak memiliki akses dari kelas dalam ke turunan kelas luar.

Cara penyelesaiannya di java adalah seperti ini:

class OuterClass {
    private int outerField = 1337;

    public class InnerClass {
        public int accessOuter() {
            return OuterClass.this.outerField;
        }
    }
}

Namun tidak ada yang setara dengan OuterClass.this.outerField dalam skrip ketikan/javascript.
Lihatlah kelas dalam skrip ketikan lebih mirip kelas dalam statis di java, namun di sini juga Anda hanya dapat mengakses properti publik:

class OuterClass {
    public static outerField = 1337; // has to be public

    public InnerClass = class { 
        public accessOuter() {
            return OuterClass.outerField;
        }
    }
}

Anda dapat meneruskan sebuah instance dari kelas luar ke kelas dalam:

class OuterClass {
    public outerField = 1337;

    public InnerClass = class {
        constructor(private parent: OuterClass) {}

        public accessOuter() {
            return this.parent.outerField;
        }
    }
}

Namun sekali lagi, Anda harus memiliki outerField publik.


Sunting

Jika Anda ingin mencapai sesuatu yang akan mensimulasikan perilaku yang diperlukan (yaitu, instance kelas dalam akan memiliki akses ke anggota kelas luar privat), maka Anda dapat melakukan sesuatu seperti ini:

interface OuterClassProxy {
    outerField: number;
}

interface IInnerClass {}

class OuterClass {
    private outerField = 1337;

    static InnerClass = class implements IInnerClass {
        constructor(private parent: OuterClassProxy) {}

        public accessOuter() {
            return this.parent.outerField;
        }
    }

    public createInnerClass(): IInnerClass {
        let outerClassInstance = this;

        return new OuterClass.InnerClass({
            get outerField(): number {
                return outerClassInstance.outerField;
            },
            set outerField(value: number) {
                outerClassInstance.outerField = value;
            }
        });
    }
}

Ini pekerjaan yang cukup banyak, tapi itu akan berhasil.

person Nitzan Tomer    schedule 03.08.2016

Jawaban @Nitzan bagus. Saya hanya ingin menambahkan bahwa saya membuat ini baru-baru ini, mungkin ini membantu:

class Outer {

    constructor() {
        this.val = 1337;
    }

    get Inner() {
        let Outer = this;
        return class {
            accessVal() { return Outer.val; }
        }
    }

}

new (new Outer()).Inner().accessVal(); // 1337
person Kosmas Papadatos    schedule 26.10.2016
comment
Mungkin ide bagus untuk menambahkan antarmuka untuk kelas dalam tempat ia digunakan - person eddiewould; 09.07.2017

Inilah cara yang benar untuk melakukan ini di TypeScript:

class OuterClass {
  private outerField = 1337;

  get InnerClass() {
    const thatOuterField = this.outerField // <-- Notice this addition
    return   class {
      public accessOuter() {
        return thatOuterField; // outerField not defined
      }
    }
  }


}

let outer = new OuterClass();
let inner = new outer.InnerClass();
let win = inner.accessOuter();

alert(win); // test works!

Tidak perlu ada yang berbelit-belit.

person Somo S.    schedule 15.04.2017
comment
ini adalah pendekatan yang sama yang telah diposting Kosmas 6 bulan sebelumnya - person phil294; 24.11.2017

Yang ini tidak bekerja terlalu buruk bagi saya:

function use<T>(value: T) {return new class {with<U>(f: (value: T) => U) {return f(value)}}}

class OuterClass {
    private outerField = 1337;

    InnerClass = use(this).with(outerThis => class { 
        accessOuter() {
            return outerThis.outerField; // outerField not defined
        }
    }
}
const outer = new OuterClass()
const inner = new outer.InnerClass()
const win = inner.accessOuter()
console.log(win)
person Nirvana    schedule 16.04.2021