0

I have class/viewModel created with Typescript. I made one field in this class as private in order to skip it when I try to get all other class properties.

Is it right and how can I skip my private property?

Object.keys(myObject).forEach(property => {
        //some stuff
     }
});

Example of my class :

class MyModel{
    id: any = ko.observable('');
    name: any = ko.observable('');

    address: any = ko.observable('');
    city: any = ko.observable('');
    state: any = ko.observable('');
    country: any = ko.observable('');

    private secretField= ko.observable('');
}
demo
  • 6,038
  • 19
  • 75
  • 149

4 Answers4

5

private keyword affects only visibility in TypeScript and doesn't affect JS output.

For class properties that weren't defined on the prototype and thus can't be modified with class property decorators, the most straightforward way is to use _ naming convention for private properties:

class MyModel {
    // ...
    private _secretField = ko.observable('');
}

Object.keys(myObject)
    .filter(key => !(typeof key === 'string' && key.charAt(0) === '_'))
    .forEach(property => {
        // some stuff
    });
Davide Cannizzo
  • 2,826
  • 1
  • 29
  • 31
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
4

TypeScript compiles private properties just like regular properties, the enforcement of the privateness is only done compile time, and they are still there in runtime.

There are a lot of requests on github to make private properties inaccessible even in runtime, but due to design limitations and/or philosophical issues this has not been implemented and it might never be.

You can read some design discussion history here.

That means that you have to use a convention of your own to handle this, for example like prefixing the name with underscore and filtering that in your loop.

Alex
  • 14,104
  • 11
  • 54
  • 77
0

You can use Symbols starting from ES6. It can store value and won't appear in Object.keys result

Js Code

const privateStuff = Symbol()
var obj = {
  name: "Andrew",
  age: 23,
  [privateStuff]: "Don't show it"  
}

var keys = Object.keys(obj);
keys.forEach((k)=>{console.log(k)});

//get value
var serverStuff=obj[privateStuff] 
0

TL;DR: Use private identifiers to define your private fields. This way, their accessibility will be enforced at runtime as well.


As it has already been pointed out, accessibility is only enforced statically (at compile–time) by the TypeScript transpiler.
Therefore, all properties, either public or private, are emitted as normal JavaScript properties. There's no magic here, and you cannot do anything to hide them except using a Proxy to trap the ownKeys() method or using Object.defineProperties rather than declaring them the TypeScript way. For the latter idea, I've come up with an example:

class Foo {

    constructor() {

        Object.defineProperties(this, {
            bar: {
                enumerable: false,
                value: "Hello world"
            }
        })

        console.log((this as any).bar)
    }
}

The above example can be tested in the TypeScript Playground.

However, I think it's an anti–pattern to do such a thing, since it breaks all the TypeScript safety, which is the only reason why opt for it rather than just writing out JavaScript code.

Therefore, the only solution we're left with is using private identifiers. This is a TypeScript feature such that any field whose name starts with # is enforced as private not only at compile–time but even at runtime.

class Foo {

    #bar = "Hello world"

    constructor() {
        console.log(this.#bar)
    }
}

console.log(Object.keys(new Foo()))

The above example can be tested in the TypeScript Playground.

How does that work? Well, you may just take a look at the transpiled JavaScript code and you'll suddenly notice a WeakMap. Indeed, a reference to a new WeakMap is defined for every field in a class with a private identifier, in the same lexical scope where their declaring class is defined. Wherever the private field is accessed, it's done by calling a getter or setter function that uses a given map (passed when referencing that field) to get or set the value at a given key, which is the instance of the class to access the field on. A runtime check is also made in both the getter and setter functions such that a TypeError is thrown when calling with a non–registered receiver to prevent private fields from being accessed with an instance of a different type or no instance at all.

Davide Cannizzo
  • 2,826
  • 1
  • 29
  • 31