Skip to content

An internal property modifier as an alternative to protectedΒ #64478

Description

πŸ” Search Terms

  • protected alternative
  • internal

Related issues:

βœ… Viability Checklist

⭐ Suggestion

Currently, protected causes several issues:

  • you can't use duck typing when a class have a protected property. You can't implement it, you are required to extend it.
  • protected (and private) properties often disappear when manipulating types.

I'd like an internal modifier that would behave similarly to readonly.
It would declare the property is part of the public interface, but you can't access it outside of its methods (a.k.a. functions where the type of this is the type of the instance).

πŸ“ƒ Motivating Example

Such feature could be used for several usages.

Having friends

Currently, TS has no friend, even though this seems to be a frequently requested feature:

An internal modifier would be a cleaner way to have friends:

  1. you declare a function/class with some internal properties.
  2. you can safely cast such object by removing the internal modifier with some type manipulations (e.g. with -internal).

You can then select exactly which properties are accessibles from friends (which helps respecting encapsulation).

Implementing classes with internal properties

Sometimes we want to implement a class instead of extending it. However, this is not possible when using protected properties:

Defining interfaces with internal properties

We could also define interfaces with internal properties, e.g. hooks that could be overridden when extending a class implementing the interface, without even requiring to know the class true concrete type.

Manipulating internal properties

This would also enable more complex type manipulations, e.g.:

πŸ’» Use Cases

Currently, there are several workarounds.

Cast

const internal = target as FooInternals;

Can be dangerous. A "type map" can be used to make the cast slightly more secure, but this is still unsafe.

Public properties

Making the properties public solves most of the issue, however, this makes properties publicly accessible, loosing some protections.

Symbols

We can hide the internal type behind a symbol.

class Foo {
      [SYMBOL] = this as InternalFoo;
      // or
       [SYMBOL] = { ... }
}

However, this is not ideal when manipulating classes in mixins (attributes aren't accessible from the prototype, and we can't really manipulate the constructor).

Hooks in the constructor

In some cases, you can give hooks in the base class constructor instead of overriding protected methods.

class Foo {
      [HOOKS]: XX; // not in the FooClass interface.

      constructor(hooks) {
             this[HOOKS] = hooks;
      }
}

But same as previously, this is not ideal when manipulating classes in mixins.

Static properties

Another way is to declare static instance methods that can be redefined:

class X {
      static onChange = () => {};
      foo() {
            this.constructor.onChange.call(this); // not type safe.
      }
}

However this is not truly type safe:

Alternative suggestion :

This issue could also be solved by specifying a public and an internal type for the same property:

The usage could then be something like foo: $PUB_TYPE @internally=$INTERNAL_TYPE, e.g.:

  • foo: never @internally=number.
  • foo: readonly number @internally=number.
  • foo: X @internally=XInternals.

However this would require bigger changes to TS.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions