Skip to content

Commit 4dc6a8d

Browse files
committed
gatby typescript: content complete draft
1 parent e51151e commit 4dc6a8d

2 files changed

Lines changed: 284 additions & 107 deletions

File tree

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
---
2+
title: Adding Typescript to your Gatbsy application
3+
date: "2020-05-10"
4+
author: franleplant
5+
description: "Docker has become a widely used
6+
technology and chances are you are going to have to deal with it eventually, at least superficially, in your Front End
7+
career. Let's cover the basic concepts and day to day useful commands you will likely use when dealing with Docker."
8+
tags:
9+
- gatsbyjs
10+
- javascript
11+
- react
12+
- typescript
13+
- graphql
14+
---
15+
16+
## Intro
17+
18+
I have been using Typescript for the past 4 years in different production
19+
projects in different companies and although this post is not meant to be
20+
a _why typescript?_ post let me tell you briefly why I love typescript
21+
and why I do think that most medium to largse size Javascript applications
22+
would benefit from using Typescript, in no particular order:
23+
24+
1- automatically up-to-date and synchronized code documentation in the form of types.
25+
2- new code verification tool added to your pool (in javascript apps this is mostly made of different kinds of tests and lints)
26+
3- better development experience: better autocompletion and api discovery
27+
4- type checker guided hassle free refactoring
28+
5- improve the mind set of developers so that they think more about data structures and their semantic meaning inside the code (as oposed to just have a bunch of unamed and uncategorized objects which is something that tends to happen in bigger javascript apps).
29+
30+
I plan to talk a lot more about this in a follow up post so that is why I wont focus to much
31+
explaining these points, but please note that most of these come from real world experiences
32+
handling large production code bases in both Javascript and Typescript, and there is an abysm between the too.
33+
34+
Additionally, from a business standpoint we can also enumerate derived benefits:
35+
36+
- more productivity: items 1, 3 and 4
37+
- higher quality: items 2, 4 and 5
38+
39+
Having these in mind we are going to cover how to integrate Typescript to Gatsby,
40+
considering that Gatbsy is a framework that can accomodote larger production applications that
41+
will benefit the most out of this.
42+
43+
The process is rather simple but I want to cover some paint points and tips and tricks.
44+
45+
Note: This blog is actually using Typescript.
46+
47+
## How to use Typescript in your Gatbsy.js site.
48+
49+
We are going to install two plugins:
50+
51+
- [gatsby-plugin-typescript](https://www.gatsbyjs.org/packages/gatsby-plugin-typescript/) to support typescript compilation
52+
- [gatsby-plugin-graphql-codegen](https://www.gatsbyjs.org/packages/gatsby-plugin-graphql-codegen/) to automatically generate Typescript types out of the underlying graphql data model that Gatsby uses.
53+
54+
After following the instructions you will have most of the tools, the rest is going to be
55+
related to 1) configuration and 2) migrating js files.
56+
57+
## Configuration
58+
59+
### Typescript
60+
61+
You will need to create a `tsconfig.json` at the root of your project configuring the
62+
typescript compiler. This can vary a lot depending on your taste but here's the one we are using:
63+
64+
```json
65+
{
66+
"compilerOptions": {
67+
"target": "esnext",
68+
"module": "commonjs",
69+
"jsx": "preserve",
70+
"strict": true,
71+
"noImplicitAny": true,
72+
"strictNullChecks": false,
73+
"strictFunctionTypes": true,
74+
"noImplicitThis": true,
75+
"alwaysStrict": true,
76+
"noUnusedLocals": true,
77+
"noUnusedParameters": true,
78+
"noImplicitReturns": true,
79+
"noFallthroughCasesInSwitch": true,
80+
"allowSyntheticDefaultImports": true,
81+
"esModuleInterop": true
82+
},
83+
"include": ["src/**/*"]
84+
}
85+
```
86+
87+
I wont cover all the options but I do want to cover `strictNullChecks`.
88+
I found that enabling this option to be a big pain when convining it with the GraphQL codegen tool.
89+
Most of the automatically generated types from the queries you make around your codebase
90+
will have a lot of optionals and `Maybe` making it super verbose and sometimes super
91+
awkard to handle all these in a strict way.
92+
93+
I usually try to use `strictNullChecks: true` for applications because it makes the typechecking
94+
to provide even more guarantees about your code, but in this particular application I recommend
95+
you avoid it altogether.
96+
97+
At this stage you might want to `allowJs: true` to avoid having to migrate _all_ your codebase
98+
at once. But you should probably migrate some of the core files just to verify the setup.
99+
100+
The process is rather simple, any `.js` file that jas `JSX` inside it should be renamed to `.tsx`,
101+
if not, it should be rename to `.ts`.
102+
103+
Let the compiler tell you what it expects from those freshly migrated files (see next section): spoiler alert,
104+
it will ask for props definitions and perhaps some dependencies type definitions.
105+
106+
### `declarations.d.ts`
107+
108+
In most Typescript codebases we have some global types or some untyped modules to declare,
109+
that is why you most likely will need `src/declaration.d.ts`. Let's see a bair minimum one:
110+
111+
```typescript
112+
// Make typescript aware of this
113+
// global Gatbsy variable
114+
declare var __PATH_PREFIX__: string
115+
116+
// Declare that this modules are untyped
117+
declare module "typography-theme-github"
118+
declare module "vfile-message"
119+
// This makes importing svg images
120+
// typecheck. You might need
121+
// to declare other types of files
122+
// used in your codebase
123+
declare module "*.svg"
124+
```
125+
126+
And then you should import this file from your `gatsby-browser.js` which
127+
is the main entry point of the browser application:
128+
129+
```javascript
130+
import "./src/declarations.d.ts"
131+
```
132+
133+
### Typechecking
134+
135+
As the typescript plugin docs state, by default it wont run typechecks on your code, it
136+
will only strip out any type information and typescript sintax and compile it down to Javascript.
137+
138+
For enabling typechecking you will need to create an npm script:
139+
140+
```json
141+
"typecheck": "tsc --noEmit",
142+
```
143+
144+
`tsc` is the Typescript Compiler and `noEmit` tells it that we only want to typecheck.
145+
146+
I also call this script from my `test` script because I want to always pass this verification,
147+
which as one of the reasons I had to add it.
148+
149+
You will probably need to install type definitions for some libraries you might be using by
150+
`yarn add --dev @types/my-lib` if they exist or simply declaring them as untyped.
151+
When typechecking your code base the typescript compiler will let you know what to do
152+
pretty acurately in each case.
153+
154+
**NOTE**: I configured this in March 2020 and I had some troubles with Gatbsy type definitions, in
155+
order to fix it I had to add this to my `package.json#resolutions` field:
156+
157+
```json
158+
"resolutions": {
159+
"unified": "7.1.0",
160+
"vfile": "4.0.3",
161+
"@types/vfile": "4.0.0"
162+
},
163+
```
164+
165+
If you see a type error relating these libraries this might be fix for you.
166+
167+
### gatsby-plugin-typescript
168+
169+
I did not made any special configurations to this plugin.
170+
171+
### gatsby-plugin-graphql-codegen
172+
173+
Here is where things become more complicated.
174+
175+
The main data layer of Gatbsy is Grapqhl and most of the pages
176+
and some components will acess that data layer and that data will
177+
flow through props and context down the component tree.
178+
179+
So it is reasonable to expect to somehow have thata data model
180+
and the queries we make out of it to by statically typed.
181+
182+
You can of course write these types by hand but it will be much better
183+
to have a tool automatically generate those files from the latest queries
184+
and GraphQL data model. Enter: `gatsby-plugin-graphql-codegen`.
185+
186+
This is how I recommend you configure it:
187+
188+
```typescript
189+
{
190+
resolve: `gatsby-plugin-graphql-codegen`,
191+
options: {
192+
codegenConfig: {
193+
// optional if you prefix, as I do,
194+
// all your types with I
195+
typesPrefix: "I",
196+
avoidOptionals: true,
197+
},
198+
},
199+
}
200+
```
201+
202+
The `avoidOptionals` is important because the codegen tools generate
203+
most of the types with the following shape:
204+
205+
```typescript
206+
export type SomeGeneratedType = {
207+
attribute?: Maybe<Type>
208+
}
209+
```
210+
211+
This evaluates to something like `Type | null | undefined`, so you basically have three potential types instead
212+
of just the two you actually epect: `Type | null` which is what `Maybe<Type>` resolves to.
213+
214+
So this will reduce some of the more awkard errors when passing props.
215+
216+
Additionally, it is better to create your own types instead of trying to reuse the types that the
217+
codegen genrates.
218+
219+
For example in this blog we have an `data/authors.yaml`.
220+
The codegen generates this type
221+
222+
```typescript
223+
export type IAuthorYaml = INode & {
224+
id: Scalars["ID"]
225+
parent: Maybe<INode>
226+
children: Array<INode>
227+
internal: IInternal
228+
bio: Maybe<Scalars["String"]>
229+
profilepicture: Maybe<IFile>
230+
twitter: Maybe<Scalars["String"]>
231+
github: Maybe<Scalars["String"]>
232+
}
233+
```
234+
235+
but the pages that make the query might generate different variantes of this type. Sometime shaving
236+
some fields off sometimes others and in particular the profilepicture filed whihc maps to an image
237+
might be altered in several ways like asking for a sharp image fluid.
238+
239+
IN that case this type wont be useful at all.
240+
INstead what I have done is to manually write a concrete `IAuthor` type
241+
that I defined which is very similar to this but only has the "domain" attributes I need
242+
243+
```typescript
244+
export interface IAuthor {
245+
id: string
246+
bio: string
247+
twitter: string
248+
github: string
249+
profilepicture?: any
250+
}
251+
```
252+
253+
and in the case of providing a particular image flui I can extend that type adhoc like this
254+
255+
```typescript
256+
interface IAuthorWithProfilePic extends IAuthor {
257+
profilepicture: {
258+
childImageSharp: {
259+
fluid: IGatsbyImageSharpFluidFragment
260+
}
261+
}
262+
}
263+
```
264+
265+
This will force you a litte bit to always query the author with all the fields except `profilepicture` which
266+
is a rather simple cost to pay to have your codebase have greater type safety guarantees.
267+
268+
Note that in the latter case `author.profilepicture` will have all the type information about
269+
what Gatbsy does with the image, with a properly configured IDE, you will be able to see how
270+
this object is made without leaving your dev tools and googling it (in this aprticular case it
271+
is actually not easy to find exactly how this object looks like)
272+
273+
### How to incorporate typechecking to your `npm run test`
274+
275+
The only important considerationg here is that you need to codegen
276+
your GraphQL types before being able to typecheck your codebase:
277+
278+
```json
279+
{
280+
"test": "npm run build && npm run typecheck"
281+
}
282+
```
283+
284+
And of course you can mix this with lints, prettier and actual unit or integration tests.

content/drafts/gatbsy-typescript/index.md

Lines changed: 0 additions & 107 deletions
This file was deleted.

0 commit comments

Comments
 (0)