Implementing TypeScript
Faust.js provides support for TypeScript including built-in types for Templates, Blocks and more.
Using graphql-codegen
First things first, you should consider using @graphql-codegen to generate types for the GraphQL queries.
Below is a sample config for generating the relevant typings:
//codegen.ts
import { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {
schema: "https://faustexample.wpengine.com/graphql",
documents: ["src/**/*.{tsx,ts}"],
generates: {
"./src/__generated__/": {
preset: "client",
plugins: [],
presetConfig: {
gqlTagName: "gql",
},
},
},
ignoreNoDocuments: true,
};
export default config;
Code language: JavaScript (javascript)
Add the following npm script that works by scanning the src
folder for GraphQL queries and generating a bunch of files inside /src/__generated__/
for the TypeScript types:
// package.json
{
"scripts": {
...
"generate": "graphql-codegen",
}
}
Code language: JavaScript (javascript)
Note: Be sure to enable WPGraphQL introspection before running the npm run generate
command since it is disabled by default.
The most important file is the graphql.ts
which contains all the schema types from the WPGraphQL endpoint plus the types of the queries:
// /src/__generated__/graphql.ts
...
export type GetPostQueryVariables = Exact<{
databaseId: Scalars['ID'];
asPreview?: InputMaybe<Scalars['Boolean']>;
}>;
export type GetPostQuery = { __typename?: 'RootQuery', post?: { __typename?: 'Post', title?: string | null, content?: string | null, date?: string | null, author?: { __typename?: 'NodeWithAuthorToUserConnectionEdge', node: { __typename?: 'User', name?: string | null } } | null } | null, generalSettings?: { __typename?: 'GeneralSettings', title?: string | null, description?: string | null } | null, primaryMenuItems?: { __typename?: 'RootQueryToMenuItemConnection', nodes: Array<{ __typename?: 'MenuItem', id: string, uri?: string | null, path?: string | null, label?: string | null, parentId?: string | null, cssClasses?: Array<string | null> | null, menu?: { __typename?: 'MenuItemToMenuConnectionEdge', node: { __typename?: 'Menu', name?: string | null } } | null }> } | null };
Code language: JavaScript (javascript)
You can use these types with the FaustTemplate
helper which we will explain next.
How to apply types for WP Template Pages
When creating a new WP Template page, you can use the FaustTemplate
to declare the type of the function component passing the type of the GraphQL query that was generated for that page:
// src/wp-templates/single.tsx
import { gql } from "../__generated__";
import { GetPostQuery } from "../__generated__/graphql";
import { FaustTemplate } from "@faustwp/core";
const Component: FaustTemplate<GetPostQuery> = (props) => {
...
}
Code language: JavaScript (javascript)
Then you can inspect all the types in the props
parameters as you type:
All the data from the query results will be properly typed based on the introspected schema:
How to apply types for block components
Similarly, when creating Block components using @faustwp/blocks
packages, you can use the WordPressBlock
type that will include all the relevant properties of that block:
// src/wp-blocks/CoreParagraph.tsx
import { gql } from "../__generated__";
import { WordPressBlock } from "@faustwp/blocks";
import { CoreParagraphFragmentFragment } from "../__generated__/graphql";
const CoreParagraph: WordPressBlock<CoreParagraphFragmentFragment> = (
props
) => {
return <p>{props.attributes?.content}</p>;
};
export const fragments = {
entry: gql(`
fragment CoreParagraphFragment on CoreParagraph {
attributes {
content
}
}
`),
key: `CoreParagraphFragment`,
};
Code language: JavaScript (javascript)
Here we pass the CoreParagraphFragmentFragment
type that corresponds to the CoreParagraphFragment
fragment mapping all fields to TypeScript types. Then TypeScript will only allow the declared types to be used in the props
parameter.
How to apply types for the plugin system
Faust providers a FaustHooks
type that you can use for applying the corresponding type of the hooks
parameter:
// src/plugins/ProjectTemplatePlugin.ts
import { FaustHooks, FaustPlugin } from '@faustwp/core';
export class ProjectTemplatePlugin implements FaustPlugin {
constructor() {}
apply(hooks: FaustHooks) {
hooks.addFilter("possibleTemplatesList", "faust", (templates, data) => {
if (data?.seedNode?.__typename === "Project") {
return Array.from(new Set(["project", ...templates]));
}
return templates;
});
}
}
Code language: JavaScript (javascript)
Here the hooks
parameter will autocomplete all correct types from each filter that is provided by the framework:
How to migrate existing pages to TypeScript
In general terms, most of the strategies for migrating existing pages to TypeScript should follow the relevant guide described in the TypeScript Docs.
To summarise, you should use the following types available:
FaustTemplate
: For WP Template pages.WordPressBlock
: For Block components.GetStaticProps
,GetServerSideProps
andGetStaticPaths
: For the result type of the Next.jsgetStaticProps
,getServerSideProps
andgetStaticPaths
functions.FaustHooks
: For the Plugin system hooks.
Let’s see an example of how to type the [...wordpressNode].tsx
page:
// src/pages/[...wordpressNode].tsx
import { getWordPressProps, WordPressTemplate } from "@faustwp/core";
import { GetStaticPaths, GetStaticProps } from "next";
export type WordPressTemplateProps = Parameters<typeof WordPressTemplate>[0];
export default function Page(props: WordPressTemplateProps) {
return <WordPressTemplate {...props} />;
}
export const getStaticProps: GetStaticProps = (ctx) => {
return getWordPressProps({ ctx });
};
export const getStaticPaths: GetStaticPaths = () => {
return {
paths: [],
fallback: "blocking",
};
};
Code language: JavaScript (javascript)
Here, since we are not exposing the type parameters of the WordPressTemplate
function, you will need to extract them using the Parameters utility type:
export type WordPressTemplateProps = Parameters<typeof WordPressTemplate>[0];
Code language: JavaScript (javascript)