Question

How to make type inference work on a piece of code?

I have this (simplified) piece of code:

export abstract class Scheduler<TPayload> {}

export interface EventKey<T> extends Symbol {}

export type SystemSpecification<TPayload> = (
  args: {
    /** The value provided by current scheduler tick */
    payload: TPayload;
  },
) => void;

export function defineSystem<TPayload>(
  system: SystemSpecification<TPayload>,
  scheduler: new () => Scheduler<TPayload>
) {
  // ...
}

const on = <TPayload>(eventKey: EventKey<TPayload>) =>
  class extends Scheduler<TPayload> {
    // ...
  };

const clickEvent = Symbol('clickEvent') as EventKey<{ foo: 20 }>;

defineSystem(
  ({ payload }) => console.log(payload.foo),
  on(clickEvent)
);

I am expecting payload value to be infered from clickEvent type : {foo: number}, but instead, I have an error: 'payload' is of type unknown

enter image description here

Am I doing something wrong? Is it actually possible to infer payload from clickEvent type?

Playground

 2  49  2
1 Jan 1970

Solution

 3

Here is how you can fix it.

Define constructor type SchedulerCtor and make sure you use it as type for scheduler: argument in defineSystem and as return type for on. Also have a look at trailing comma — <TPayload,>



export abstract class Scheduler<TPayload> {}

export interface EventKey<T> extends Symbol {}

export type SystemSpecification<TPayload> = (
  args: {
    /** The value provided by current scheduler tick */
    payload: TPayload;
  },
) => void;

type SchedulerCtor<TPayload> = new () => Scheduler<TPayload>

export function defineSystem<TPayload>(
  system: SystemSpecification<TPayload>,
  scheduler: SchedulerCtor<TPayload>
) {
  // ...
}

const on = <TPayload,>(eventKey: EventKey<TPayload>): SchedulerCtor<TPayload> => {
  return class extends Scheduler<TPayload> {
    // ...
  };
};

const clickEvent = Symbol('clickEvent') as EventKey<{ foo: number }>;

defineSystem(
  ({ payload }) => console.log(payload.foo),
  on(clickEvent)
);

2024-07-19
Iaroslav Sobolev