Skip to content

Commit

Permalink
Docs: TypeScript Development Guide (#1383)
Browse files Browse the repository at this point in the history
* Docs: TypeScript Development Guide

* Docs: Add missing code

* Update internal/faustjs.org/docs/guides/typescript.mdx

Co-authored-by: Blake Wilson <blake.wilson@wpengine.com>

* Update internal/faustjs.org/docs/guides/typescript.mdx

Co-authored-by: Blake Wilson <blake.wilson@wpengine.com>

* Update internal/faustjs.org/docs/guides/typescript.mdx

Co-authored-by: Blake Wilson <blake.wilson@wpengine.com>

---------

Co-authored-by: Blake Wilson <blake.wilson@wpengine.com>
  • Loading branch information
theodesp and blakewilson committed Apr 17, 2023
1 parent d52fcdb commit 7500e0d
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 0 deletions.
190 changes: 190 additions & 0 deletions internal/faustjs.org/docs/guides/typescript.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
---
slug: /guides/typescript
title: TypeScript
description: Typescript development guide for Faust.js
---

Faust.js provides support for [TypeScript](https://www.typescriptlang.org/) including built-in types for Templates, Blocks and more.

- [View the Faust.js TypeScript scaffold application](https://github.com/wpengine/faust-scaffold-ts)

## Using `graphql-codegen`

First things first, you should consider using [@graphql-codegen](https://the-guild.dev/graphql/codegen) to generate types for the GraphQL queries.

Below is a sample config for generating the relevant typings:

```ts title="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;
```

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:

```json title="package.json"
{
"scripts": {
...
"generate": "graphql-codegen",
}
}
```


:::note

Be sure to enable WPGraphQL introspection before running the `npm run generate` command since it is [disabled by default](https://www.wpgraphql.com/docs/security#introspection-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:

```ts title="/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 };

```

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:

```ts title="src/wp-templates/single.tsx"
import { gql } from "../__generated__";

import { GetPostQuery } from "../__generated__/graphql";
import { FaustTemplate } from "@faustwp/core";

const Component: FaustTemplate<GetPostQuery> = (props) => {
...
}
```

Then you can inspect all the types in the `props` parameters as you type:

<img src="/docs/img/typescript-faust-template-props.png" alt="FaustTemplate prop types" />

All the data from the query results will be properly typed based on the introspected schema:

<img src="/docs/img/typescript-faust-template-props-data-type.png" alt="FaustTemplate prop data types" />

## 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:

```ts title="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`,
};
```

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:

```ts title="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;
});
}
}
```

Here the `hooks` parameter will autocomplete all correct types from each filter that is provided by the framework:

<img src="/docs/img/typescript-fausthooks-type.png" alt="FaustHooks prop data types" />

## 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](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html) 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` and `GetStaticPaths`: For the result type of the Next.js `getStaticProps`, `getServerSideProps` and `getStaticPaths` functions.
* `FaustHooks`: For the Plugin system hooks.

Let's see an example of how to type the `[...wordpressNode].tsx` page:

```ts title="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",
};
};
```

Here, since we are not exposing the type parameters of the `WordPressTemplate` function,
you will need to extract them using the [Parameters](https://www.typescriptlang.org/docs/handbook/utility-types.html#parameterstype) utility type:

```ts
export type WordPressTemplateProps = Parameters<typeof WordPressTemplate>[0];
```
5 changes: 5 additions & 0 deletions internal/faustjs.org/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ module.exports = {
label: 'Sitemaps',
id: 'guides/sitemaps',
},
{
type: 'doc',
label: 'TypeScript',
id: 'guides/typescript',
},
{
type: 'doc',
label: 'Customizing the Toolbar',
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

1 comment on commit 7500e0d

@atlas-by-wp-engine
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check out the recent updates to your Atlas environment:

App Environment URL Build
faustjs canary https://hg…wered.com ✅ (logs)

Learn more about building on Atlas in our documentation.

Please sign in to comment.