Just Alex
Just Alex

Reputation: 362

TypeScript: combine behavior of type and implementation

I have the following code:

type MyClassType = {
  state: 0;
  data: {
    p1: boolean;
  };
} | {
  state: 1;
  data: {
    p2: boolean;
  };
} | {
  state: 2;
  data: {
    p3: boolean;
  };
}

class MyClassImplementation {
  state = 0;
  data = {
    p1: true
  };
}

export const MyClass = MyClassImplementation as {
  new (): MyClassType;
}

function a(c: MyClassImplementation) {
  // works fine, but without completion I want
  if (c.state === 1) {
    c.data.p2; // error: Property 'p2' does not exist on type '{ p1: boolean; }'.
  }
}

The following code works the way I want:

function b(c: MyClassType) {
  if (c.state === 1) {
    c.data.p2; // works fine
  }
}

The following code gives me an error: 'MyClass' refers to a value, but is being used as a type here. Did you mean 'typeof MyClass'?

function c(c: MyClass) {
}

Is there a way to combine behavior of MyClassType and MyClassImplementation? I want to have right suggestions and create new instances using single entity (MyClass)? So I don't want to export MyClassType and MyClassImplementation, only MyClass.

Upvotes: 1

Views: 51

Answers (1)

jcalz
jcalz

Reputation: 328067

My suggestion would be to rename MyClassType to MyClass and export that as well as your MyClass value:

export type MyClass = { /* your union of things */ }

export const MyClass = MyClassImplementation as {
  new(): MyClass;
}

This will allow those who import MyClass to get both the named constructor value and the named instance type, similarly to how class names and types work:

import { MyClass } from 'mymodule';
const c: MyClass = new MyClass(); // okay

Playground link to code

Upvotes: 2

Related Questions