Extend source's Data interface with types from new handlers

This is my last playground in case you want to check something.

Why would you pass Data as a parameter to Handler? Shouldn’t you pass a Package?

Yeah, well… Source needs Data for both state.source.get and state.source.data so maybe it’s easier for people extending Data to have a generic than to redefine those things themselves:

How are you doing that right now?

Modifying Package is only needed for source packages, like wp-source so it’s going to be less common.

So maybe we could do this: Source<MyData, MyPackage>. Both optional of course.

I see. Yeah, I guess that the most common need is to extend data so it makes sense to create a shortcut for that. But in the case of the handler, when you need to pass the Package as well, you’ll have Data defined in both generics. Not sure if that makes sense. I’d stick to only passing Package as generic, so you only worry to wire your custom data to one place, and after that, anything else will just use your package.

I mean, this way you’ll be reducing all the scenarios to just one, which seems more simple to me. If you implemented Handler with two generics, people will need to understand that there is one scenario where you are only extending Data in Source, and that’s the only custom thing you’ll use in the Handler, and the second scenario where you might have extended Post too, and you need both in the handler so you need to pass ExtendedData and CustomPackage.

This is what I’d recommend to do in every types.ts file of a package as a good practice:

// Types

import WpSource, { Handler as WpSourceHandler } from "@frontity/wp-source/types";

export default interface Theme extends Package {
  name: "my-theme";
  state: {
    theme: {};
    source?: WpSource<ThemeData>["state"]["source"];
  };
}

export type FC<Props extends object = {}> = React.FC<
  Connect<Theme, Props>
>;

export type Handler = WpSourceHandler<Theme>;
// Component

import * as Theme from '../types';

const Component: Theme.FC = () => <div></div>;

// or

import { FC } from '../types';

const Component: FC = () => <div></div>;
// Handler

import * as Theme from '../types';

const handler: Theme.Handler = {
  name: "my-handle",
  pattern: "/custom-path",
  func: async () => {}
};

// or

import { Handler } from '../types';

const handler: Handler = { ... };

And that’s all they need to use while developing in my experience.

EDIT:
I also think that recommending to create aliases of types in the types.ts file is a good idea, because it can become a bit difficult to keep in mind all the packages that are used and need to be imported, and this way they only need to think about the typing once when creating the basic types.ts file and then just importing from there whatever they need.

Also this basic aliases can be generated automatically with the frontity CLI so developers can start working from there.

Just to make sure I understand you correctly. You think that:

  • Using a generic for Source is fine, like this Source<ExtendedData>.
  • You won’t use a generic for Data in the Handler type, you’d pass only the package, like this: Handler<MyPackage>.

Is that true?

By the way, are you using the types of WpSource instead of Source to populate state.source? If so, is there any reason?

That’s correct.

Well, because the API of WpSource extends Source and includes Handler. I assumed that I should use the types of the package I have installed, and not those of the base.

EDIT:
I didn’t update yet, so I don’t know if this has changed.

No it hasn’t, but that’s useful information for Source v2. Thanks!

It seems like we may need to use the same approach for Entities as well as for Data in the case of state.source.entity(link).

More information in the FD that introduced this API: Support for Yoast plugin REST API fields.