diff --git a/docs/docs/typescript.md b/docs/docs/typescript.md new file mode 100644 index 0000000000000..4c69bfd99e874 --- /dev/null +++ b/docs/docs/typescript.md @@ -0,0 +1,35 @@ +--- +title: TypeScript and Gatsby +--- + +## Introductory paragraph + +[TypeScript](https://www.typescriptlang.org/) is a JavaScript superset which extends the language to include type definitions allowing codebases to be statically checked for soundness. Gatsby provides an integrated experience out of the box, similar to an IDE. If you are new to TypeScript, adoption can _and should_ be incremental. Since Gatsby natively supports JavaScript and TypeScript, you can change files from .js to .tsx at any point to start adding types and gaining the benefits of a type system. + +## Example + +```tsx:title=src/pages/index.js +import React from "react" +import { PageProps } from "gatsby" + +export default function IndexRoute(props: PageProps) { + return ( + <> +
{props.path}
+ > + ) +} +``` + +The example above uses the power of TypeScript, in combination with exported types from Gatsby (`PageProps`) to tell this code what props is. This can greatly improve your developer experience by letting your IDE show you what properties are injected by Gatsby. To see all of the types available look at our [TypeScript definition file](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/index.d.ts). + +## Other resources + +TypeScript integration is supported through automatically including [`gatsby-plugin-typescript`](/plugins/gatsby-plugin-typescript). Visit that link to see configuration options and limitations of this setup. + +If you are new to TypeScript, check out these other resources to learn more: + +- [TypeScript Documentation](https://www.typescriptlang.org/docs/handbook/basic-types.html) +- [TypeScript Playground (Try it out!)](https://www.typescriptlang.org/play/index.html) +- [TypeScript Gatsby Example](https://using-typescript.gatsbyjs.org/) diff --git a/e2e-tests/development-runtime/cypress/integration/hot-reloading/arrow-functions.js b/e2e-tests/development-runtime/cypress/integration/hot-reloading/arrow-functions.js index 24bb9fd3ef57e..723ebd7563694 100644 --- a/e2e-tests/development-runtime/cypress/integration/hot-reloading/arrow-functions.js +++ b/e2e-tests/development-runtime/cypress/integration/hot-reloading/arrow-functions.js @@ -18,7 +18,7 @@ describe(`hot-reloading anonymous arrow functions`, () => { it(`updates on change`, () => { const text = `The title` cy.exec( - `npm run update -- --file src/components/title.js --replacements "TITLE:${text}"` + `npm run update -- --file src/components/title.tsx --replacements "TITLE:${text}"` ) cy.getTestElement(IDS.title).invoke(`text`).should(`eq`, text) diff --git a/e2e-tests/development-runtime/src/components/title.js b/e2e-tests/development-runtime/src/components/title.tsx similarity index 100% rename from e2e-tests/development-runtime/src/components/title.js rename to e2e-tests/development-runtime/src/components/title.tsx diff --git a/examples/using-typescript/gatsby-config.js b/examples/using-typescript/gatsby-config.js index f379f286837f5..0e9ac8e1df3a8 100644 --- a/examples/using-typescript/gatsby-config.js +++ b/examples/using-typescript/gatsby-config.js @@ -4,7 +4,9 @@ module.exports = { exampleUrl: `https://github.com/gatsbyjs/gatsby/tree/master/examples/using-typescript`, }, plugins: [ - `gatsby-plugin-typescript`, + // `gatsby-plugin-typescript` is automatically included in gatsby + // You only need to explicitly define it here if you need to configure + // specific options in it { resolve: `gatsby-plugin-typography`, options: { diff --git a/examples/using-typescript/package.json b/examples/using-typescript/package.json index 8a9677987996d..e3b19effcd98b 100644 --- a/examples/using-typescript/package.json +++ b/examples/using-typescript/package.json @@ -14,7 +14,6 @@ "sideEffects": false, "dependencies": { "gatsby": "^2.21.0", - "gatsby-plugin-typescript": "^2.4.0", "gatsby-plugin-typography": "^2.5.0", "react": "^16.12.0", "react-dom": "^16.12.0", diff --git a/packages/gatsby-plugin-typescript/README.md b/packages/gatsby-plugin-typescript/README.md index 85d0b47cf1083..4f81598fe6622 100644 --- a/packages/gatsby-plugin-typescript/README.md +++ b/packages/gatsby-plugin-typescript/README.md @@ -2,26 +2,15 @@ Allows Gatsby to build TypeScript and TSX files. Does NOT run type checking during build (see Caveats). -## Install +This plugin is automatically included in Gatsby. The only reason you would need to explicitly use this plugin is if you need to configure its options. -`npm install gatsby-plugin-typescript` +## How to customize usage -## How to use - -1. Include the plugin in your `gatsby-config.js` file. +1. Include the plugin in your `gatsby-config.js` file with the specific options 1. Write your components in TSX or TypeScript. 1. Run TypeScript directly or with a build tool. 1. You're good to go. -`gatsby-config.js` - -```javascript -module.exports = { - // ..., - plugins: [`gatsby-plugin-typescript`], -} -``` - > When [creating pages programmatically](/docs/programmatically-create-pages-from-data/#creating-pages), you can pass the `.tsx` filename directly as the `component` for [`createPage`](/docs/actions/#createPage). _**Please note**: If packages don't ship with TypeScript definitions you'll need to manually install those type definitions, e.g. for React. A typical Gatsby project would need: `npm install --save-dev @types/react @types/react-dom @types/node`_ diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 6a44a9b4e0f20..f6ed26dc782f3 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -75,6 +75,7 @@ "gatsby-core-utils": "^1.2.1", "gatsby-graphiql-explorer": "^0.4.1", "gatsby-link": "^2.4.1", + "gatsby-plugin-typescript": "^2.4.0", "gatsby-plugin-page-creator": "^2.3.1", "gatsby-react-router-scroll": "^2.3.1", "gatsby-telemetry": "^1.3.2", diff --git a/packages/gatsby/src/bootstrap/load-plugins/__tests__/__snapshots__/load-plugins.js.snap b/packages/gatsby/src/bootstrap/load-plugins/__tests__/__snapshots__/load-plugins.js.snap index b46918821e8d2..bee8007693173 100644 --- a/packages/gatsby/src/bootstrap/load-plugins/__tests__/__snapshots__/load-plugins.js.snap +++ b/packages/gatsby/src/bootstrap/load-plugins/__tests__/__snapshots__/load-plugins.js.snap @@ -167,6 +167,22 @@ Array [ "ssrAPIs": Array [], "version": "1.0.0", }, + Object { + "browserAPIs": Array [], + "id": "", + "name": "gatsby-plugin-typescript", + "nodeAPIs": Array [ + "resolvableExtensions", + "onCreateBabelConfig", + "onCreateWebpackConfig", + ], + "pluginOptions": Object { + "plugins": Array [], + }, + "resolve": "", + "ssrAPIs": Array [], + "version": "1.0.0", + }, Object { "browserAPIs": Array [], "id": "", @@ -381,6 +397,22 @@ Array [ "ssrAPIs": Array [], "version": "1.0.0", }, + Object { + "browserAPIs": Array [], + "id": "", + "name": "gatsby-plugin-typescript", + "nodeAPIs": Array [ + "resolvableExtensions", + "onCreateBabelConfig", + "onCreateWebpackConfig", + ], + "pluginOptions": Object { + "plugins": Array [], + }, + "resolve": "", + "ssrAPIs": Array [], + "version": "1.0.0", + }, Object { "browserAPIs": Array [], "id": "", @@ -601,6 +633,22 @@ Array [ "ssrAPIs": Array [], "version": "1.0.0", }, + Object { + "browserAPIs": Array [], + "id": "", + "name": "gatsby-plugin-typescript", + "nodeAPIs": Array [ + "resolvableExtensions", + "onCreateBabelConfig", + "onCreateWebpackConfig", + ], + "pluginOptions": Object { + "plugins": Array [], + }, + "resolve": "", + "ssrAPIs": Array [], + "version": "1.0.0", + }, Object { "browserAPIs": Array [], "id": "", diff --git a/packages/gatsby/src/bootstrap/load-plugins/__tests__/load-plugins.js b/packages/gatsby/src/bootstrap/load-plugins/__tests__/load-plugins.js index 598eb580ab436..5b55e3e381a7d 100644 --- a/packages/gatsby/src/bootstrap/load-plugins/__tests__/load-plugins.js +++ b/packages/gatsby/src/bootstrap/load-plugins/__tests__/load-plugins.js @@ -71,4 +71,97 @@ describe(`Load plugins`, () => { expect(plugins).toMatchSnapshot() }) + + describe(`TypeScript support`, () => { + it(`loads gatsby-plugin-typescript if not provided`, async () => { + const config = { + plugins: [], + } + + let plugins = await loadPlugins(config) + + plugins = replaceFieldsThatCanVary(plugins) + + expect(plugins).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + browserAPIs: [], + id: ``, + name: `gatsby-plugin-typescript`, + nodeAPIs: [ + `resolvableExtensions`, + `onCreateBabelConfig`, + `onCreateWebpackConfig`, + ], + pluginOptions: { + plugins: [], + }, + resolve: ``, + ssrAPIs: [], + version: `1.0.0`, + }), + ]) + ) + }) + + it(`uses the user provided plugin-typescript if provided`, async () => { + const config = { + plugins: [ + { + resolve: `gatsby-plugin-typescript`, + options: { + jsxPragma: `h`, + }, + }, + ], + } + + let plugins = await loadPlugins(config) + + plugins = replaceFieldsThatCanVary(plugins) + + expect(plugins).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + browserAPIs: [], + id: ``, + name: `gatsby-plugin-typescript`, + nodeAPIs: [ + `resolvableExtensions`, + `onCreateBabelConfig`, + `onCreateWebpackConfig`, + ], + pluginOptions: { + plugins: [], + jsxPragma: `h`, + }, + resolve: ``, + ssrAPIs: [], + version: `1.0.0`, + }), + ]) + ) + }) + + it(`does not add gatsby-plugin-typescript if it exists in config.plugins`, async () => { + const config = { + plugins: [ + `gatsby-plugin-typescript`, + { resolve: `gatsby-plugin-typescript` }, + ], + } + + let plugins = await loadPlugins(config) + + plugins = replaceFieldsThatCanVary(plugins) + + const tsplugins = plugins.filter( + plugin => plugin.name === `gatsby-plugin-typescript` + ) + + // TODO: I think we should probably be de-duping, so this should be 1. + // But this test is mostly here to ensure we don't add an _additional_ gatsby-plugin-typescript + expect(tsplugins.length).toEqual(2) + }) + }) }) diff --git a/packages/gatsby/src/bootstrap/load-plugins/load.js b/packages/gatsby/src/bootstrap/load-plugins/load.js index 0591cffc217f1..f4edc1df48cf2 100644 --- a/packages/gatsby/src/bootstrap/load-plugins/load.js +++ b/packages/gatsby/src/bootstrap/load-plugins/load.js @@ -243,6 +243,21 @@ const loadPlugins = (config = {}, rootDir = null) => { } } + // TypeScript support by default! use the user-provided one if it exists + const typescriptPlugin = (config.plugins || []).find( + plugin => + plugin.resolve === `gatsby-plugin-typescript` || + plugin === `gatsby-plugin-typescript` + ) + + if (typescriptPlugin === undefined) { + plugins.push( + processPlugin({ + resolve: require.resolve(`gatsby-plugin-typescript`), + }) + ) + } + plugins.push( processPlugin({ resolve: require.resolve(`gatsby-plugin-page-creator`), diff --git a/starters/blog/src/pages/index.js b/starters/blog/src/pages/index.tsx similarity index 77% rename from starters/blog/src/pages/index.js rename to starters/blog/src/pages/index.tsx index 092d19347b0c4..fbcaf03980ac8 100644 --- a/starters/blog/src/pages/index.js +++ b/starters/blog/src/pages/index.tsx @@ -1,12 +1,36 @@ +// Gatsby supports TypeScript natively! import React from "react" -import { Link, graphql } from "gatsby" +import { PageProps, Link, graphql } from "gatsby" import Bio from "../components/bio" import Layout from "../components/layout" import SEO from "../components/seo" import { rhythm } from "../utils/typography" -const BlogIndex = ({ data, location }) => { +type Data = { + site: { + siteMetadata: { + title: string + } + } + allMarkdownRemark: { + edges: { + node: { + excerpt: string + frontmatter: { + title: string + date: string + description: string + } + fields: { + slug: string + } + } + }[] + } +} + +const BlogIndex = ({ data, location }: PageProps) => { const siteTitle = data.site.siteMetadata.title const posts = data.allMarkdownRemark.edges diff --git a/starters/default/src/pages/page-2.js b/starters/default/src/pages/page-2.tsx similarity index 61% rename from starters/default/src/pages/page-2.js rename to starters/default/src/pages/page-2.tsx index 666c23ef30f9d..025b50ed2b095 100644 --- a/starters/default/src/pages/page-2.js +++ b/starters/default/src/pages/page-2.tsx @@ -1,14 +1,15 @@ +// Gatsby supports TypeScript natively! import React from "react" -import { Link } from "gatsby" +import { PageProps, Link } from "gatsby" import Layout from "../components/layout" import SEO from "../components/seo" -const SecondPage = () => ( +const SecondPage = (props: PageProps) => (Welcome to page 2
+Welcome to page 2 ({props.path})
Go back to the homepage