Luciens
Luciens

Reputation: 348

How to export a class as readonly but enable internal modifying?

Since my class has async init process. I have to define a property readonly public but modify it internally like

declare function asyncAppMap(target: string): Promise<Window>

class FrameInternal {
  public readonly content: Window | null = null
  constructor(target: string) {
    asyncAppMap(target).then(app => {
      // Not possible currently.
      // this.content = app
    })
  }
}

I am looking for some reusable utility like:

class FrameInternal{}
// Then all members of Frame becomes readonly
export const Frame: ReadonlyClass<FrameInternal> = FrameInternal

Upvotes: 2

Views: 376

Answers (1)

jcalz
jcalz

Reputation: 330481

Depending on your specific needs, you could use something like this:

type ReadonlyClass<T extends new (...args: any) => any> =
    T extends new (...args: infer A) => infer R ?
    new (...args: A) => { readonly [K in keyof R]: R[K] } : never;

The type ReadonlyClass converts a constructor type to a new constructor type that produces the same instance type with all its properties marked as readonly.

When you export a class constructor you probably also want to export a type with the same name, corresponding to the instance type. This happens automatically when you use the class Foo {} notation (Foo is introduced as both the name of the class constructor value, and as the name of the class instance type):

export const Frame: ReadonlyClass<typeof FrameInternal> = FrameInternal;
export type Frame = InstanceType<typeof Frame>;

Which, when you inspect it, looks like this:

/*
const Frame: new (target: string) => {
    readonly content: Window | null;
}
type Frame = {
    readonly content: Window | null;
}
*/

That should work at least in the example case you showed above:

const f = new Frame("z");
f.content = null; // error, can't assign to read only

function acceptFrame(f: Frame) {
    f.content = null; // error here too    
}

Okay, hope that helps; good luck!

Link to code

Upvotes: 2

Related Questions