Remix.run Logo
seanmcdirmid 5 hours ago

TS doesn’t really. TS simply treats private fields as public ones when it comes to structural type checks. TS is unsound anyways, so not providing hard guarantees about field access safety is right up its alley. More to the point, if you specify a class type with private fields as a requirement, whatever you plug into that requirement has to have those private fields, they are part of the type’s public signature.

To get where structural type systems fall down, think about a bad case is when dealing with native state and you have a private long field with a pointer hiding in it used in native calls. Any “type” that provides that long will fit the type, leading to seg faults. A nominal type system allows you to make assurances behind the class name.

Anyways, this was a big deal in the late 90s, eg see opaque types https://en.wikipedia.org/wiki/Opaque_data_type.

b_e_n_t_o_n 4 hours ago | parent [-]

Typescript had to support JS's quirks... :/

   class Foo {
      public bar = 1;
      private _value = 'hello';
      static doSomething(f: Foo) {
         console.log(f._value);
      }
   }
   class MockFoo { public bar = 1; }
   let mock = new MockFoo();
   Foo.doSomething(mock); // Fails
Which is why you'd generally use interfaces, either declared or inline.

In the pointer example, if the long field is private then it's not part of the public interface and you shouldn't run into that issue no?

seanmcdirmid 3 hours ago | parent [-]

_value is part of the type for Foo, it’s as if it was a public field. You can forge a reference to Foo by adding _value to your mock. TS deals with private fields by pretending they are public when it comes to matching. There are more rigorous ways have hiding and then revealing private state in structurally typed languages, but they involve something that is suspiciously like using a name, and really, it makes sense. The only way you can hide something and recover it later is via some kind of name (unless you can somehow capture the private structure in a type variable so it’s just passing through the parts that can’t see it).

You can do a lot just by hiding the private state and providing methods that operate on that private state in the type (using interfaces for example), but that approach doesn’t allow for binary methods (you need to reveal private state on a non-receiver in a method).

b_e_n_t_o_n 3 hours ago | parent [-]

Can you explain the last part more? I don't think I'm grasping what you mean.