0

I am using a library called apprun. The Component class type defintion (here is its implementation) looks as follow:

  export type View<T> = (state: T) => string | VNode | VNode[] | void;

  export class Component<T=any> {
    constructor(state?: T, view?: View<T>, update?: Update<T>);
    readonly state: T;
    setState(state: T, options?: { render?: boolean, history?: boolean }): void;
    mount(element?: Element, options?: { render?: boolean, history?, global_event?: boolean }): Component<T>;
    start(element?: Element, options?: { render?: boolean, history?, global_event?: boolean }): Component<T>;
    on(name: string, fn: (...args: any[]) => void, options?: any): void;
    run(name: string, ...args: any[]): number;
    rendered: (state: T) => void;
    mounted: (props: any) => void;
    unmount: () => void;
    unload: () => void;
  }

When I use this class in my own components, I am having issues with the type inference:

interface State {
  foo: string
}

export class AboutPage extends Component<State> {
  // State is correctly inferred.
  // Changing `foo` to be `1` will cause an error.
  state = {
    foo: 'bar'
  }

  // Parameter `state` implicitly has an 'any' type.
  // Why is the parameter not inferred just like the `state` property?
  view = (state) => <div>About</div>
}

What I am having trouble with is understanding why the type for the property state is inferred and why isn't the same thing happening for the parameter state?

r0skar
  • 8,338
  • 14
  • 50
  • 69
  • Is the `view` argument in `Component`'s constructor supposed to be declared as a member (with e.g. a `public` modifier)? However, even with the modifier added, the type of the `state` argument to `view` in `AboutPage` is inferred to be `any`. Overloading and LSP may be at play. – outis Apr 11 '19 at 03:23

1 Answers1

1

That is because state is defined as Component's class property: readonly state: T. But view is defined only in your code, where is no type definition, so it's type is inferred from view = (state) => <div>About</div> declaration, and so it is (state: any) => JSX.Element.

You should define view: View<T> in your own Component class that will be inherited from Component, or define the type of state argument: state: State.

wrager
  • 813
  • 1
  • 7
  • 25
  • But isn't it already defined in the lib's `Component` class: `constructor(state?: T, view?: View, update?: Update);`? What I am having trouble with is understanding why the type for the property `state` is inferred and why isn't the same thing happening for the parameter `state`? – r0skar Apr 10 '19 at 07:51
  • 1
    @r0skar That is because `state` is defined as `Component`'s class property `readonly state: T`. But the `view` is defined only in your code, where is no type definition, so it's type is inferred from `view = (state) =>
    About
    ` declaration.
    – wrager Apr 10 '19 at 07:56
  • Oh I see. Thanks for clearing things up. One last thing: you say I should define `view: View` in my own Component (which works). But when why isnt it working when I define `view: View;` (the same way `state` is defined) in the lib's Component declaration file? – r0skar Apr 10 '19 at 08:03
  • I have no idea since I can't see the code. Is there an error in the declaration file or the same problem as in the question? – wrager Apr 10 '19 at 08:12
  • The same problem as in the question. The parameter type in my Component is not inferred. I think it is the same issue as described [here](https://stackoverflow.com/questions/52123541/typescript-infer-function-parameters-in-derived-class) and on [github](https://github.com/Microsoft/TypeScript/issues/23911) and out of scope for my original question. – r0skar Apr 10 '19 at 08:14
  • Hmm, yes, it seems like that problem. – wrager Apr 10 '19 at 08:18