Skip to content

feature request: runtime privacy #31914

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
3 of 5 tasks
trusktr opened this issue Jun 14, 2019 · 6 comments
Closed
3 of 5 tasks

feature request: runtime privacy #31914

trusktr opened this issue Jun 14, 2019 · 6 comments
Labels
Out of Scope This idea sits outside of the TypeScript language design constraints Suggestion An idea for TypeScript

Comments

@trusktr
Copy link
Contributor

trusktr commented Jun 14, 2019

Search Terms

runtime privacy

Suggestion

It'd be great there was a compiler option to enable runtime privacy.

When this mode is enabled, the compiled output would use simple WeakMap-based privates.

Alternatively, a builtin decorator could turn it on/off on a per/class basis.

Use Cases

It happens often that someone writes a library in TypeScript, but the end user consumes the library in plain JavaScript without the intended privacy.

Examples

Nothing changes syntactically. For example:

export class Foo {
  private foo = 'foo'
  private bar() {
    console.log(this.foo)
  }
  test() {
    this.bar()
  }
}

consumer code:

import {Foo} from './Foo'

const f: any = new Foo() as any

console.log(f.foo) // undefined
f.test() // it works
f.bar() // runtime error, undefined is not a function.

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code, because the new option defaults to false.
  • This wouldn't change the runtime behavior of existing JavaScript code, because the new option defaults to false.
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals, because it does not add new or modify existing syntax, it only extends privacy to runtime when an option is set to true.

More

There are already private fields with # that are potentially coming to the language, but many people won't want to use it over private syntax, plus many source codes already use private and it may be inconvenient to convert them all over once # lands.

An option would be great, and the implementation can be light-weight, with a simple WeakMap implementation as follows:

The above snippet could compile to something like the following, using a single WeakMap per module scope no matter how many classes are inside the file (unlike Babel's which uses one WeakMap per private field which is a bit excessive):

const privates = new WeakMap()

function _(inst) {
  let priv = privates.get(inst)
  if (!priv) privates.set(inst, priv = {})
  return priv
}

export class Foo {
  constructor() {
    _(this).foo = 'foo'
    _(this).bar = () => {
      console.log(_(this).foo)
    }
  }
  test() {
    _(this).bar()
  }
}

consumer code:

import {Foo} from './Foo'

const f = new Foo()

console.log(f.foo) // undefined
f.test() // it works
f.bar() // runtime error, undefined is not a function.

That's the simplest implementation with clean class-like output.

Here's another one that makes one method per class, instead of one per instance:

const privates = new WeakMap()

function _(inst) {
  let priv = privates.get(inst)
  if (!priv) privates.set(inst, priv = {})
  return priv
}

function Foo_bar() {
  console.log(_(this).foo)
}

export class Foo {
  constructor() {
    _(this).foo = 'foo'
  }
  test() {
    Foo_bar.call(this)
  }
}
@nattthebear
Copy link

it may be inconvenient to convert them all over once # lands.

I'm sure someone will write something that trivializes this, whether in a quick fix or elsewhere.

@DanielRosenwasser DanielRosenwasser added Out of Scope This idea sits outside of the TypeScript language design constraints Suggestion An idea for TypeScript labels Jun 15, 2019
@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Jun 15, 2019

Part of the reason that # is a required prefix in ECMAScript privates is that it indicates it's tied to the lexically contained declaration. private is meant to be erasable type-level syntax for property accesses.

but many people won't want to use it over private syntax,

Can you elaborate on this? I understand migration costs (the next point you make), but we can make that easier with refactorings. Why would you want # semantics but not use ECMAScript syntax?

@trusktr
Copy link
Contributor Author

trusktr commented Jun 15, 2019

Can you elaborate on this?

tc39/proposal-class-fields#100

It is the most disliked thing to land in JS, looks like.

private is meant to be erasable type-level syntax for property accesses.

It doesn't have to be meant that way, and because it is exclusive to TS, it would be fine to do it a new way (that doesn't break existing TS code).

@trusktr
Copy link
Contributor Author

trusktr commented Jun 15, 2019

deleted, there's lots of reasons why people don't like # in tc39/proposal-class-fields#100.

@RyanCavanaugh
Copy link
Member

The technical justifications behind # are extremely strong. That thread is perhaps the saddest I've ever seen in terms of loud and angry voices shouting down people calmly explaining the necessary reasons behind #.

@trusktr
Copy link
Contributor Author

trusktr commented Jun 25, 2019

It is sad for both sides. The # is tangential to this feature request anyways.

TypeScript could use WeakMaps to make runtime privates, and it'd be awesome (as long as it is optional).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Out of Scope This idea sits outside of the TypeScript language design constraints Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants