feat: enable the use of TypeScript in this repo (#604)

* feat: enable Typescript in this repo

* refactor: rename studio-header files to .ts[x]

* chore: fix minor type warnings

* chore: add types for frontend-platform

* chore: fix type issues

* chore: update name of suppressed lint check
This commit is contained in:
Braden MacDonald
2025-07-21 10:24:52 -07:00
committed by GitHub
parent f47c1ed1e6
commit 171a770235
24 changed files with 75 additions and 9 deletions

View File

@@ -24,6 +24,8 @@ jobs:
run: make validate-no-uncommitted-package-lock-changes
- name: Lint
run: npm run lint
- name: Type check
run: npm run types
- name: Test
run: npm run test
- name: Build

View File

@@ -25,6 +25,8 @@ jobs:
run: make validate-no-uncommitted-package-lock-changes
- name: Lint
run: npm run lint
- name: Type check
run: npm run types
- name: Test
run: npm run test
- name: i18n_extract

View File

@@ -13,7 +13,8 @@
"lint:fix": "fedx-scripts eslint --fix --ext .js --ext .jsx .",
"snapshot": "fedx-scripts jest --updateSnapshot",
"start": "fedx-scripts webpack-dev-server --progress",
"test": "fedx-scripts jest --coverage"
"test": "fedx-scripts jest --coverage",
"types": "tsc --noEmit"
},
"files": [
"/dist"

View File

@@ -22,7 +22,7 @@ import messages from '../Header.messages';
import { CaretIcon } from '../Icons';
class DesktopHeader extends React.Component {
constructor(props) { // eslint-disable-line no-useless-constructor
constructor(props) { // eslint-disable-line @typescript-eslint/no-useless-constructor
super(props);
}

41
src/frontend-platform.d.ts vendored Normal file
View File

@@ -0,0 +1,41 @@
// frontend-platform currently doesn't provide types... do it ourselves for i18n module at least.
// We can remove this in the future when we migrate to frontend-shell, or when frontend-platform gets types
// (whichever comes first).
declare module '@edx/frontend-platform/i18n' {
// eslint-disable-next-line import/no-extraneous-dependencies
import { injectIntl as _injectIntl } from 'react-intl';
/** @deprecated Use useIntl() hook instead. */
export const injectIntl: typeof _injectIntl;
/** @deprecated Use useIntl() hook instead. */
export const intlShape: any;
// eslint-disable-next-line import/no-extraneous-dependencies
export {
createIntl,
FormattedDate,
FormattedTime,
FormattedRelativeTime,
FormattedNumber,
FormattedPlural,
FormattedMessage,
defineMessages,
IntlProvider,
useIntl,
} from 'react-intl';
// Other exports from the i18n module:
export const configure: any;
export const getPrimaryLanguageSubtag: (code: string) => string;
export const getLocale: (locale?: string) => string;
export const getMessages: any;
export const isRtl: (locale?: string) => boolean;
export const handleRtl: any;
export const mergeMessages: any;
export const LOCALE_CHANGED: any;
export const LOCALE_TOPIC: any;
export const getCountryList: any;
export const getCountryMessages: any;
export const getLanguageList: any;
export const getLanguageMessages: any;
}

View File

@@ -21,7 +21,7 @@ import messages from '../Header.messages';
import { MenuIcon } from '../Icons';
class MobileHeader extends React.Component {
constructor(props) { // eslint-disable-line no-useless-constructor
constructor(props) { // eslint-disable-line @typescript-eslint/no-useless-constructor
super(props);
}

View File

@@ -34,7 +34,7 @@ describe('BrandNav Component', () => {
it('displays a link that navigates to studioBaseUrl', () => {
render(<RootWrapper />);
const link = screen.getByRole('link');
const link = screen.getByRole('link') as HTMLAnchorElement;
expect(link.href).toBe(studioBaseUrl);
});
});

View File

@@ -16,7 +16,7 @@ const mockProps = {
const RootWrapper = (props) => (
<MemoryRouter>
<IntlProvider locale="en" messages={messages}>
<IntlProvider locale="en" messages={{}}>
<CourseLockUp {...props} />
</IntlProvider>
</MemoryRouter>
@@ -52,7 +52,8 @@ describe('CourseLockUp Component', () => {
it('navigates to an absolute URL when clicked', () => {
render(<RootWrapper {...mockProps} />);
const link = screen.getByTestId('course-lock-up-block');
// FIXME: don't use testId - https://testing-library.com/docs/queries/about#priority
const link = screen.getByTestId('course-lock-up-block') as HTMLAnchorElement;
expect(link.href).toBe(mockProps.outlineLink);
});
});

View File

@@ -35,7 +35,7 @@ const defaultProps = {
const RootWrapper = (props) => (
<MemoryRouter>
<IntlProvider locale="en" messages={messages}>
<IntlProvider locale="en" messages={{}}>
<HeaderBody {...props} />
</IntlProvider>
</MemoryRouter>

View File

@@ -135,6 +135,7 @@ const HeaderBody = ({
logoutUrl,
authenticatedUserAvatar,
isAdmin,
isMobile,
}}
/>
</Nav>

View File

@@ -13,6 +13,7 @@ const MobileHeader = ({
return (
<>
{/* @ts-expect-error The type of 'props' is any until we convert from propTypes to TypeScript interface/types */}
<HeaderBody
{...props}
isMobile

View File

@@ -26,7 +26,7 @@ let screenWidth = 1280;
const RootWrapper = ({
...props
}) => {
}: React.ComponentProps<typeof StudioHeader>) => {
const appContextValue = useMemo(() => ({
authenticatedUser: currentUser,
config: {
@@ -55,7 +55,7 @@ const RootWrapper = ({
);
};
const props = {
const props: React.ComponentProps<typeof StudioHeader> = {
number: '123',
org: 'Ed',
title: 'test',
@@ -74,6 +74,10 @@ const props = {
outlineLink: 'tEsTLInK',
searchButtonAction: null,
isNewHomePage: true,
// These default values shouldn't be needed but typescript is confused by propTypes; can remove after converting
// from propTypes to TypeScript:
containerProps: {},
isHiddenMainMenu: false,
};
describe('Header', () => {

View File

@@ -19,6 +19,7 @@ const StudioHeader = ({
number, org, title, containerProps, isHiddenMainMenu, mainMenuDropdowns,
outlineLink, searchButtonAction, isNewHomePage,
}) => {
// @ts-expect-error - frontend-platform doesn't yet have type information :/
const { authenticatedUser, config } = useContext(AppContext);
const props = {
logo: config.LOGO_URL,

12
tsconfig.json Normal file
View File

@@ -0,0 +1,12 @@
{
"extends": "@edx/typescript-config",
"compilerOptions": {
"noEmit": true,
"baseUrl": "./src",
"paths": {
"*": ["*"]
}
},
"include": ["*.js", ".eslintrc.js", "src/**/*", "plugins/**/*"],
"exclude": ["dist", "node_modules"]
}