1

I have an interface as:

export interface PostEventTypeMap {
  changePrice: number;
  changeQuantity: number;
  signalConnected: boolean;
  signalDisconnected: boolean;
}

how I can define a type alias like:

type PostEvent = {
  type: keyof PostEventTypeMap,
  value: **Value based on key value and PostEventTypeMap**
};

I can not make postEvent property generic:

PostBoxService {
  get postEvent(): Observable<PostEvent> {
    return getOrCreateValue(this, "postEvent", () => new BehaviorSubject(null));
  }
}

The usecase of service:

postBoxService
  .postEvent
  .subscribe(event => {
    if (!event)
      return;

    switch (event.type) {
      case "changePrice":
        this.changePrice(event.value);
        break;
      case "changeQuantity":
        this.changeQuantity(event.value);
        break;
    }
  });

2 Answers 2

3

Because you have a finite number of possibilities, and since the type value will always be a literal type, you can make this a discriminated union. One way to do this is to map the PostEventTypeMap properties to the union members you want, and then immediately look up these properties to get a union:

type ObjToTypeValueUnion<T extends object> = 
  { [K in keyof T]-?: { type: K, value: T[K] } }[keyof T]

type PostEvent = ObjToTypeValueUnion<PostEventTypeMap>;
/* type PostEvent = {
    type: "changePrice";
    value: number;
} | {
    type: "changeQuantity";
    value: number;
} | {
    type: "signalConnected";
    value: boolean;
} | {
    type: "signalDisconnected";
    value: boolean;
} */

You can verify that this works as desired for your use case:

declare function changePrice(x: number): void;
declare function changeQuantity(x: number): void;
((event?: PostEvent) => {
  if (!event)
    return;

  switch (event.type) {
    case "changePrice":
      changePrice(event.value);
      break;
    case "changeQuantity":
      changeQuantity(event.value);
      break;
  }
});

Playground link to code

Sign up to request clarification or add additional context in comments.

Comments

1

You may need to make the PostEvent type generic, as in:

export interface PostEventTypeMap {
  changePrice: number;
  changeQuantity: number;
  signalConnected: boolean;
  signalDisconnected: boolean;
}

type PostEvent<T extends keyof PostEventTypeMap> = {
  type: T,
  value: PostEventTypeMap[T]
};

const validChangePrice: PostEvent<'changePrice'> = {
  type: 'changePrice',
  value: 1
}

const invalidChangePrice: PostEvent<'changePrice'> = {
  type: 'changePrice',
  value: true // Compile error TS2322: Type 'true' is not assignable to type 'number'.
}

1 Comment

Thank you, but I do not want to make it generic. because I use this type as return type of getter property

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.