Skip to content

Commit 1654e43

Browse files
authored
initial ts setup. changed pages/*.js to pages/*.tsx (#1792)
1 parent 07f2c9a commit 1654e43

40 files changed

+471
-153
lines changed

.eslintrc.js

+50-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module.exports = {
99
'plugin:jest/recommended',
1010
'plugin:cypress/recommended',
1111
'plugin:storybook/recommended',
12+
'plugin:@typescript-eslint/eslint-recommended',
1213
],
1314
env: {
1415
browser: true,
@@ -27,12 +28,58 @@ module.exports = {
2728
'@operation_code/custom-rules',
2829
'import',
2930
'lodash',
31+
'@typescript-eslint',
3032
],
3133
globals: {
3234
cy: true,
3335
Cypress: true,
3436
},
3537
overrides: [
38+
{
39+
files: ['./**/*.ts', './**/*.tsx'],
40+
parser: '@typescript-eslint/parser',
41+
parserOptions: {
42+
project: true,
43+
},
44+
extends: ['plugin:@typescript-eslint/strict', 'plugin:@typescript-eslint/stylistic'],
45+
rules: {
46+
'react/no-array-index-key': 'off',
47+
'react/require-default-props': 'off',
48+
49+
// Typescript Rules
50+
// custom rules for typescript-eslint: https://github.com/OperationCode/front-end/pull/1792#pullrequestreview-1874516174
51+
'@typescript-eslint/consistent-type-imports': ['error', { prefer: 'no-type-imports' }],
52+
'@typescript-eslint/explicit-module-boundary-types': 'off',
53+
'@typescript-eslint/naming-convention': [
54+
'error',
55+
{
56+
selector: 'variable',
57+
types: ['boolean'],
58+
format: ['PascalCase', 'UPPER_CASE'],
59+
prefix: [
60+
'is',
61+
'was',
62+
'should',
63+
'has',
64+
'can',
65+
'did',
66+
'will',
67+
'IS_',
68+
'WAS_',
69+
'SHOULD_',
70+
'HAS_',
71+
'CAN_',
72+
'DID_',
73+
'WILL_',
74+
],
75+
},
76+
],
77+
'@typescript-eslint/no-empty-interface': ['error', { allowSingleExtends: true }],
78+
'@typescript-eslint/no-explicit-any': 'error',
79+
'@typescript-eslint/no-unused-vars': ['error', { vars: 'all', varsIgnorePattern: '_' }],
80+
'@typescript-eslint/unbound-method': 'off', // gives false negatives in arrow funcs
81+
},
82+
},
3683
{
3784
files: ['cypress/**/*.js'],
3885
rules: {
@@ -128,7 +175,7 @@ module.exports = {
128175
],
129176
'react/forbid-prop-types': ['error', { forbid: ['any'] }],
130177
'react/jsx-curly-brace-presence': ['error', { props: 'never', children: 'never' }],
131-
'react/jsx-filename-extension': ['error', { extensions: ['.js'] }],
178+
'react/jsx-filename-extension': ['error', { extensions: ['.js', '.tsx'] }],
132179
'react/jsx-max-props-per-line': ['error', { maximum: 1, when: 'multiline' }],
133180
'react/jsx-no-target-blank': 'off', // browsers protect against this vulnerability now
134181
'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }],
@@ -197,4 +244,6 @@ module.exports = {
197244
],
198245
'no-use-before-define': 'off',
199246
},
247+
248+
root: true,
200249
};

components/Cards/Card/Card.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ Card.defaultProps = {
1313
hasAnimationOnHover: false,
1414
};
1515

16-
function Card({ children, className, hasAnimationOnHover, ...props }) {
16+
function Card({ children, className, ...props }) {
1717
const customDataAttributes = getDataAttributes(props);
1818

1919
return (
2020
<article
2121
className={twMerge(
2222
'items-center bg-white [&_svg]:fill-themeSecondary text-themeSecondary flex flex-col flex-nowrap justify-around m-4 min-h-[100px] min-w-[100px] p-6 shadow-md focus-visible:outline-none',
23-
hasAnimationOnHover &&
23+
props.hasAnimationOnHover &&
2424
'shadow-sm transition-shadow duration-200 ease-linear hover:shadow-lg focus-visible:shadow-lg',
2525
className,
2626
)}

components/Container/Container.js

+2-9
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,8 @@ Container.defaultProps = {
2121
theme: 'secondary',
2222
};
2323

24-
function Container({
25-
backgroundImageSource,
26-
children,
27-
className,
28-
id,
29-
isFullViewportHeight,
30-
theme,
31-
...props
32-
}) {
24+
function Container(props) {
25+
const { backgroundImageSource, children, className, id, isFullViewportHeight, theme } = props;
3326
// See https://css-tricks.com/tinted-images-multiple-backgrounds/ for explanation
3427
const darkOverlay = 'linear-gradient(rgba(33, 48, 69, 0.65),rgba(33, 48, 69, 0.65))';
3528
const dynamicBackgroundImage = backgroundImageSource

next-env.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/// <reference types="next" />
2+
/// <reference types="next/image-types/global" />
3+
4+
// NOTE: This file should not be edited
5+
// see https://nextjs.org/docs/basic-features/typescript for more information.

next.config.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// @ts-check
12
const hasBundleAnalyzer = process.env.ANALYZE === 'true';
23
const { withSentryConfig } = require('@sentry/nextjs');
34
const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: hasBundleAnalyzer });
@@ -127,10 +128,14 @@ const nextConfig = {
127128
* The name of the function type is `normalizeConfig` in `next/dist/server/config-shared`,
128129
* but JSDoc type imports can't read it.
129130
*
130-
* @type {(phase: string, config: import('next').NextConfig) => Promise<import('next').NextConfig>)}
131+
* @type {(phase: string, config: import('next').NextConfig) => Promise<import('next').NextConfig>}
131132
*/
132-
module.exports = (phase, defaultConfig) => {
133+
module.exports = async (phase, defaultConfig) => {
133134
const plugins = [
135+
/**
136+
*
137+
* @type {(config: import('next').NextConfig) => any}
138+
*/
134139
config => withSentryConfig(config, sentryWebpackPluginOptions),
135140
withBundleAnalyzer,
136141
];

package.json

+13-1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,17 @@
100100
"@testing-library/cypress": "^8.0.3",
101101
"@testing-library/jest-dom": "^5.16.4",
102102
"@testing-library/react": "^12.1.5",
103+
"@types/fingerprintjs2": "2",
104+
"@types/fontfaceobserver": "^2.1.3",
105+
"@types/lodash": "^4.14.202",
106+
"@types/logrocket-react": "^3.0.3",
107+
"@types/object-hash": "^3.0.0",
108+
"@types/prop-types": "^15.7.11",
109+
"@types/react": "^18.2.55",
110+
"@types/react-dom": "^18.2.19",
111+
"@types/react-select": "^4.0.18",
112+
"@typescript-eslint/eslint-plugin": "^6.21.0",
113+
"@typescript-eslint/parser": "^6.21.0",
103114
"autoprefixer": "^10.4.15",
104115
"axios-mock-adapter": "^1.21.1",
105116
"babel-jest": "^28.1.3",
@@ -114,7 +125,7 @@
114125
"css-loader": "^6.7.1",
115126
"cypress": "^10.3.1",
116127
"cypress-image-snapshot": "^4.0.1",
117-
"eslint": "^8.24.0",
128+
"eslint": "^8.56.0",
118129
"eslint-config-airbnb": "^19.0.4",
119130
"eslint-config-prettier": "^8.5.0",
120131
"eslint-plugin-cypress": "^2.12.1",
@@ -154,6 +165,7 @@
154165
"stylelint-config-standard": "^26.0.0",
155166
"stylelint-prettier": "^2.0.0",
156167
"tailwindcss": "^3.3.3",
168+
"typescript": "^5.3.3",
157169
"url-loader": "^4.1.1",
158170
"webpack": "^5.73.0"
159171
},
File renamed without changes.

pages/_app.js renamed to pages/_app.tsx

+8-14
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
/* eslint-disable max-classes-per-file */
2-
31
// Polyfills
42
import 'intersection-observer';
53

6-
import { useEffect } from 'react';
4+
import { useEffect, PropsWithChildren } from 'react';
75
import * as Sentry from '@sentry/nextjs';
8-
import { node } from 'prop-types';
96
import Router from 'next/router';
107
import Fingerprint2 from 'fingerprintjs2';
118
import FontFaceObserver from 'fontfaceobserver';
@@ -18,6 +15,8 @@ import { gtag } from 'common/utils/thirdParty/gtag';
1815
import Nav from 'components/Nav/Nav';
1916
import Footer from 'components/Footer/Footer';
2017
import 'common/styles/globals.css';
18+
import { AppProps } from 'next/app';
19+
import NextErrorComponent from 'next/error';
2120

2221
const isProduction = process.env.NODE_ENV === 'production';
2322

@@ -34,11 +33,7 @@ const fonts = [
3433
},
3534
];
3635

37-
Layout.propTypes = {
38-
children: node.isRequired,
39-
};
40-
41-
function Layout({ children }) {
36+
function Layout({ children }: PropsWithChildren<unknown>) {
4237
return (
4338
<div>
4439
<Nav />
@@ -65,16 +60,15 @@ if (!isProduction) {
6560
Router.events.on('routeChangeComplete', () => {
6661
const path = '/_next/static/chunks/styles.chunk.module.css';
6762
const chunksSelector = `link[href*="${path}"]:not([rel=preload])`;
68-
const chunksNodes = document.querySelectorAll(chunksSelector);
63+
const chunksNodes: NodeListOf<HTMLAnchorElement> = document.querySelectorAll(chunksSelector);
6964
if (chunksNodes.length) {
7065
const timestamp = new Date().valueOf();
7166
chunksNodes[0].href = `${path}?ts=${timestamp}`;
7267
}
7368
});
7469
}
7570

76-
// eslint-disable-next-line react/prop-types
77-
const App = ({ Component, pageProps, err }) => {
71+
const App = ({ Component, pageProps, err }: AppProps & { err: NextErrorComponent }) => {
7872
useEffect(() => {
7973
/* Analytics */
8074
// TODO: Leverage prod-build-time-only env vars instead of window check
@@ -91,7 +85,7 @@ const App = ({ Component, pageProps, err }) => {
9185
setupLogRocketReact(LogRocket);
9286

9387
// Per library docs, Fingerprint2 should not run immediately
94-
if (window.requestIdleCallback) {
88+
if ('requestIdleCallback' in window) {
9589
requestIdleCallback(setLogRocketFingerprint);
9690
} else {
9791
setTimeout(setLogRocketFingerprint, 500);
@@ -103,7 +97,7 @@ const App = ({ Component, pageProps, err }) => {
10397
if (font.url) {
10498
const link = document.createElement('link');
10599
link.href = font.url;
106-
link.rel = 'stylesheet'; // eslint-disable-line unicorn/prevent-abbreviations
100+
link.rel = 'stylesheet';
107101
document.head.append(link);
108102
}
109103

pages/_document.js renamed to pages/_document.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import Document, { Html, Head, Main, NextScript } from 'next/document';
1+
import Document, { Html, Head, Main, NextScript, DocumentContext } from 'next/document';
22
import { clientTokens } from 'common/config/environment';
33

44
export default class MyDocument extends Document {
5-
static async getInitialProps(ctx) {
5+
static async getInitialProps(ctx: DocumentContext) {
66
const initialProps = await Document.getInitialProps(ctx);
77

88
return initialProps;
@@ -32,7 +32,6 @@ export default class MyDocument extends Document {
3232
)}
3333
{process.env.VERCEL_ENV === 'production' && (
3434
<script
35-
// eslint-disable-next-line react/no-danger
3635
dangerouslySetInnerHTML={{
3736
__html: `
3837
window.dataLayer = window.dataLayer || [];

pages/_error.js renamed to pages/_error.tsx

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1-
import NextErrorComponent from 'next/error';
1+
import NextErrorComponent, { ErrorProps } from 'next/error';
22
import * as Sentry from '@sentry/nextjs';
33
import ErrorDisplay from 'components/ErrorDisplay/ErrorDisplay';
4+
import { NextPageContext } from 'next';
45

5-
// eslint-disable-next-line react/prop-types
6-
const CustomError = ({ statusCode, hasGetInitialPropsRun, err }) => {
6+
interface CustomErrorProps extends ErrorProps {
7+
hasGetInitialPropsRun?: boolean;
8+
err?: NextPageContext['err'];
9+
}
10+
11+
const CustomError = ({ statusCode, hasGetInitialPropsRun, err }: CustomErrorProps) => {
712
if (!hasGetInitialPropsRun && err) {
813
// getInitialProps is not called in case of
914
// https://github.com/vercel/next.js/issues/8592. As a workaround, we pass
@@ -15,8 +20,9 @@ const CustomError = ({ statusCode, hasGetInitialPropsRun, err }) => {
1520
return <ErrorDisplay statusCode={statusCode} />;
1621
};
1722

18-
CustomError.getInitialProps = async ({ res, err, asPath }) => {
19-
const errorInitialProps = await NextErrorComponent.getInitialProps({
23+
CustomError.getInitialProps = async ({ res, err, asPath, ...props }: NextPageContext) => {
24+
const errorInitialProps: CustomErrorProps = await NextErrorComponent.getInitialProps({
25+
...props,
2026
res,
2127
err,
2228
});
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

pages/confirm_email.js renamed to pages/confirm_email.tsx

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
1+
import { NextPageContext } from 'next';
12
import Link from 'next/link';
2-
import { bool } from 'prop-types';
33
import { confirmEmail } from 'common/constants/api';
44
import Head from 'components/head';
55
import HeroBanner from 'components/HeroBanner/HeroBanner';
66
import Content from 'components/Content/Content';
77
import Alert from '../components/Alert/Alert';
88

9-
ConfirmEmail.propTypes = {
10-
isVerified: bool.isRequired,
11-
};
12-
13-
ConfirmEmail.getInitialProps = async ({ query: { key } }) => {
9+
ConfirmEmail.getInitialProps = async ({ query: { key } }: NextPageContext) => {
1410
try {
1511
const data = await confirmEmail({ key });
1612

@@ -20,7 +16,7 @@ ConfirmEmail.getInitialProps = async ({ query: { key } }) => {
2016
}
2117
};
2218

23-
function ConfirmEmail({ isVerified }) {
19+
function ConfirmEmail({ isVerified }: { isVerified: boolean }) {
2420
return (
2521
<>
2622
<Head title="Email Verification" />
File renamed without changes.
File renamed without changes.

pages/faq.js renamed to pages/faq.tsx

-2
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,6 @@ function FAQ() {
314314

315315
<HeroBanner title="Frequently Asked Questions" />
316316

317-
{/* eslint-disable react/no-array-index-key */}
318317
<Content
319318
title="General Questions"
320319
hasTitleUnderline
@@ -362,7 +361,6 @@ function FAQ() {
362361
/>
363362
))}
364363
/>
365-
{/* eslint-enable react/no-array-index-key */}
366364
</div>
367365
);
368366
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)