Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[No QA] Standardize type-only imports #32475

Merged
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
15 changes: 15 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,21 @@ module.exports = {
'@typescript-eslint/switch-exhaustiveness-check': 'error',
'@typescript-eslint/consistent-type-definitions': ['error', 'type'],
'@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/consistent-type-imports': [
'error',
{
prefer: 'type-imports',
fixStyle: 'separate-type-imports',
},
],
'@typescript-eslint/no-import-type-side-effects': 'error',
'@typescript-eslint/consistent-type-exports': [
'error',
{
fixMixedExportsWithInlineTypeSpecifier: false,
},
],
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
'es/no-nullish-coalescing-operators': 'off',
'es/no-optional-chaining': 'off',
'valid-jsdoc': 'off',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Category from './Category';
import type Category from './Category';

Check failure on line 1 in .github/actions/javascript/authorChecklist/categories/index.ts

View workflow job for this annotation

GitHub Actions / lint

Definition for rule 'import/consistent-type-specifier-style' was not found
import newComponent from './newComponentCategory';

const categories: Category[] = [newComponent];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import github from '@actions/github';

Check failure on line 1 in .github/actions/javascript/authorChecklist/categories/newComponentCategory.ts

View workflow job for this annotation

GitHub Actions / lint

Definition for rule 'import/consistent-type-specifier-style' was not found
import {parse} from '@babel/parser';
import traverse from '@babel/traverse';
import CONST from '../../../../libs/CONST';
import GithubUtils from '../../../../libs/GithubUtils';
import promiseSome from '../../../../libs/promiseSome';
import Category from './Category';
import type Category from './Category';

type SuperClassType = {superClass: {name?: string; object: {name: string}; property: {name: string}} | null; name: string};

Expand Down
2 changes: 1 addition & 1 deletion assets/emojis/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Smiley from '@assets/images/emoji.svg';

Check failure on line 1 in assets/emojis/common.ts

View workflow job for this annotation

GitHub Actions / lint

Definition for rule 'import/consistent-type-specifier-style' was not found
import Flags from '@assets/images/emojiCategoryIcons/flag.svg';
import FoodAndDrink from '@assets/images/emojiCategoryIcons/hamburger.svg';
import Objects from '@assets/images/emojiCategoryIcons/light-bulb.svg';
Expand All @@ -7,7 +7,7 @@
import AnimalsAndNature from '@assets/images/emojiCategoryIcons/plant.svg';
import Activities from '@assets/images/emojiCategoryIcons/soccer-ball.svg';
import FrequentlyUsed from '@assets/images/history.svg';
import {HeaderEmoji, PickerEmojis} from './types';
import type {HeaderEmoji, PickerEmojis} from './types';

const skinTones = [
{
Expand Down
2 changes: 1 addition & 1 deletion assets/emojis/en.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {EmojisList} from './types';
import type {EmojisList} from './types';

Check failure on line 1 in assets/emojis/en.ts

View workflow job for this annotation

GitHub Actions / lint

Definition for rule 'import/consistent-type-specifier-style' was not found

/* eslint-disable @typescript-eslint/naming-convention */
const enEmojis: EmojisList = {
Expand Down
2 changes: 1 addition & 1 deletion assets/emojis/es.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {EmojisList} from './types';
import type {EmojisList} from './types';

Check failure on line 1 in assets/emojis/es.ts

View workflow job for this annotation

GitHub Actions / lint

Definition for rule 'import/consistent-type-specifier-style' was not found

/* eslint-disable @typescript-eslint/naming-convention */
const esEmojis: EmojisList = {
Expand Down
2 changes: 1 addition & 1 deletion assets/emojis/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import emojis from './common';

Check failure on line 1 in assets/emojis/index.ts

View workflow job for this annotation

GitHub Actions / lint

Definition for rule 'import/consistent-type-specifier-style' was not found
import enEmojis from './en';
import esEmojis from './es';
import {Emoji} from './types';
import type {Emoji} from './types';

type EmojiTable = Record<string, Emoji>;

Expand Down
2 changes: 1 addition & 1 deletion assets/emojis/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import IconAsset from '@src/types/utils/IconAsset';
import type IconAsset from '@src/types/utils/IconAsset';

Check failure on line 1 in assets/emojis/types.ts

View workflow job for this annotation

GitHub Actions / lint

Definition for rule 'import/consistent-type-specifier-style' was not found

type Emoji = {
code: string;
Expand Down
39 changes: 36 additions & 3 deletions contributingGuides/TS_STYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
- [1.19 Satisfies operator](#satisfies-operator)
- [1.20 Hooks instead of HOCs](#hooks-instead-of-hocs)
- [1.21 `compose` usage](#compose-usage)
- [1.22 Type imports](#type-imports)
- [Exception to Rules](#exception-to-rules)
- [Communication Items](#communication-items)
- [Migration Guidelines](#migration-guidelines)
Expand Down Expand Up @@ -383,7 +384,7 @@ type Foo = {

<a name="file-organization"></a><a name="1.15"></a>

- [1.15](#file-organization) **File organization**: In modules with platform-specific implementations, create `types.ts` to define shared types. Import and use shared types in each platform specific files. Do not use [`satisfies` operator](#satisfies-operator) for platform-specific implementations, always define shared types that complies with all variants.
- [1.15](#file-organization) **File organization**: In modules with platform-specific implementations, create `types.ts` to define shared types. Import and use shared types in each platform specific files. Do not use [`satisfies` operator](#satisfies-operator) for platform-specific implementations, always define shared types that complies with all variants.

> Why? To encourage consistent API across platform-specific implementations. If you're migrating module that doesn't have a default implement (i.e. `index.ts`, e.g. `getPlatform`), refer to [Migration Guidelines](#migration-guidelines) for further information.

Expand Down Expand Up @@ -514,7 +515,7 @@ type Foo = {
<a name="hooks-instead-of-hocs"></a><a name="1.20"></a>

- [1.20](#hooks-instead-of-hocs) **Hooks instead of HOCs**: Replace HOCs usage with Hooks whenever possible.

> Why? Hooks are easier to use (can be used inside the function component), and don't need nesting or `compose` when exporting the component. It also allows us to remove `compose` completely in some components since it has been bringing up some issues with TypeScript. Read the [`compose` usage](#compose-usage) section for further information about the TypeScript issues with `compose`.

> Note: Because Onyx doesn't provide a hook yet, in a component that accesses Onyx data with `withOnyx` HOC, please make sure that you don't use other HOCs (if applicable) to avoid HOC nesting.
Expand Down Expand Up @@ -571,7 +572,7 @@ type Foo = {
<a name="compose-usage"></a><a name="1.21"></a>

- [1.21](#compose-usage) **`compose` usage**: Avoid the usage of `compose` function to compose HOCs in TypeScript files. Use nesting instead.

> Why? `compose` function doesn't work well with TypeScript when dealing with several HOCs being used in a component, many times resulting in wrong types and errors. Instead, nesting can be used to allow a seamless use of multiple HOCs and result in a correct return type of the compoment. Also, you can use [hooks instead of HOCs](#hooks-instead-of-hocs) whenever possible to minimize or even remove the need of HOCs in the component.

```ts
Expand Down Expand Up @@ -607,6 +608,38 @@ type Foo = {
export default withCurrentUserPersonalDetails(ComponentWithReportOrNotFound);
```

<a name="type-imports"></a><a name="1.22"></a>

- [1.22](#type-imports) **Type imports/exports**: Always use the `type` keyword when importing/exporting types

> Why? In order to improve code clarity and consistency and reduce bundle size after typescript transpilation, we enforce the all type imports/exports to contain the `type` keyword. This way, TypeScript can automatically remove those imports from the transpiled JavaScript bundle

Imports:
```ts
// BAD
import {SomeType} from './a'
import someVariable from './a'

import {someVariable, SomeOtherType} from './b'

// GOOD
import type {SomeType} from './a'
import someVariable from './a'
```

Exports:
```ts
// BAD
export {SomeType}
export someVariable
// or
export {someVariable, SomeOtherType}

// GOOD
export type {SomeType}
export someVariable
```

## Exception to Rules

Most of the rules are enforced in ESLint or checked by TypeScript. If you think your particular situation warrants an exception, post the context in the `#expensify-open-source` Slack channel with your message prefixed with `TS EXCEPTION:`. The internal engineer assigned to the PR should be the one that approves each exception, however all discussion regarding granting exceptions should happen in the public channel instead of the GitHub PR page so that the TS migration team can access them easily.
Expand Down
3 changes: 2 additions & 1 deletion src/CONFIG.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Platform} from 'react-native';
import Config, {NativeConfig} from 'react-native-config';
import type {NativeConfig} from 'react-native-config';
import Config from 'react-native-config';
import CONST from './CONST';
import getPlatform from './libs/getPlatform';
import * as Url from './libs/Url';
Expand Down
10 changes: 5 additions & 5 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {OnyxEntry} from 'react-native-onyx/lib/types';
import {ValueOf} from 'type-fest';
import CONST from './CONST';
import * as OnyxTypes from './types/onyx';
import DeepValueOf from './types/utils/DeepValueOf';
import type {OnyxEntry} from 'react-native-onyx/lib/types';
import type {ValueOf} from 'type-fest';
import type CONST from './CONST';
import type * as OnyxTypes from './types/onyx';
import type DeepValueOf from './types/utils/DeepValueOf';

/**
* This is a file containing constants for all the top level keys in our store
Expand Down
4 changes: 2 additions & 2 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {IsEqual, ValueOf} from 'type-fest';
import CONST from './CONST';
import type {IsEqual, ValueOf} from 'type-fest';
import type CONST from './CONST';

// This is a file containing constants for all the routes we want to be able to go to

Expand Down
2 changes: 1 addition & 1 deletion src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* This is a file containing constants for all of the screen names. In most cases, we should use the routes for
* navigation. But there are situations where we may need to access screen names directly.
*/
import DeepValueOf from './types/utils/DeepValueOf';
import type DeepValueOf from './types/utils/DeepValueOf';

const PROTECTED_SCREENS = {
HOME: 'Home',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Str from 'expensify-common/lib/str';
import React, {useEffect, useRef} from 'react';
import {Text as RNText, StyleSheet} from 'react-native';
import type {Text as RNText} from 'react-native';
import {StyleSheet} from 'react-native';
import PressableWithSecondaryInteraction from '@components/PressableWithSecondaryInteraction';
import Text from '@components/Text';
import Tooltip from '@components/Tooltip';
Expand Down
4 changes: 2 additions & 2 deletions src/components/AnchorForCommentsOnly/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {StyleProp, TextStyle} from 'react-native';
import ChildrenProps from '@src/types/utils/ChildrenProps';
import type {StyleProp, TextStyle} from 'react-native';
import type ChildrenProps from '@src/types/utils/ChildrenProps';

type AnchorForCommentsOnlyProps = ChildrenProps & {
/** The URL to open */
Expand Down
7 changes: 4 additions & 3 deletions src/components/AnimatedStep/AnimatedStepContext.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {createContext} from 'react';
import {ValueOf} from 'type-fest';
import CONST from '@src/CONST';
import type React from 'react';
import {createContext} from 'react';
import type {ValueOf} from 'type-fest';
import type CONST from '@src/CONST';

type AnimationDirection = ValueOf<typeof CONST.ANIMATION_DIRECTION>;

Expand Down
5 changes: 3 additions & 2 deletions src/components/AnimatedStep/AnimatedStepProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, {useMemo, useState} from 'react';
import CONST from '@src/CONST';
import ChildrenProps from '@src/types/utils/ChildrenProps';
import AnimatedStepContext, {AnimationDirection} from './AnimatedStepContext';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import type {AnimationDirection} from './AnimatedStepContext';
import AnimatedStepContext from './AnimatedStepContext';

function AnimatedStepProvider({children}: ChildrenProps): React.ReactNode {
const [animationDirection, setAnimationDirection] = useState<AnimationDirection>(CONST.ANIMATION_DIRECTION.IN);
Expand Down
6 changes: 3 additions & 3 deletions src/components/AnimatedStep/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, {useMemo} from 'react';
import {StyleProp, ViewStyle} from 'react-native';
import type {StyleProp, ViewStyle} from 'react-native';
import * as Animatable from 'react-native-animatable';
import useThemeStyles from '@hooks/useThemeStyles';
import useNativeDriver from '@libs/useNativeDriver';
import CONST from '@src/CONST';
import ChildrenProps from '@src/types/utils/ChildrenProps';
import {AnimationDirection} from './AnimatedStepContext';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import type {AnimationDirection} from './AnimatedStepContext';

type AnimatedStepProps = ChildrenProps & {
/** Styles to be assigned to Container */
Expand Down
3 changes: 2 additions & 1 deletion src/components/AnimatedStep/useAnimatedStepContext.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {useContext} from 'react';
import AnimatedStepContext, {StepContext} from './AnimatedStepContext';
import type {StepContext} from './AnimatedStepContext';
import AnimatedStepContext from './AnimatedStepContext';

function useAnimatedStepContext(): StepContext {
const context = useContext(AnimatedStepContext);
Expand Down
6 changes: 3 additions & 3 deletions src/components/AnonymousReportFooter.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React from 'react';
import {Text, View} from 'react-native';
import {OnyxCollection} from 'react-native-onyx';
import {OnyxEntry} from 'react-native-onyx/lib/types';
import type {OnyxCollection} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx/lib/types';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as Session from '@userActions/Session';
import {PersonalDetails, Report} from '@src/types/onyx';
import type {PersonalDetails, Report} from '@src/types/onyx';
import AvatarWithDisplayName from './AvatarWithDisplayName';
import Button from './Button';
import ExpensifyWordmark from './ExpensifyWordmark';
Expand Down
3 changes: 2 additions & 1 deletion src/components/ArchivedReportFooter.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import lodashEscape from 'lodash/escape';
import React from 'react';
import {OnyxEntry, withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {FlashList} from '@shopify/flash-list';
import React, {ForwardedRef, forwardRef, ReactElement, useCallback, useEffect, useMemo, useRef} from 'react';
import {View} from 'react-native';
import type {ForwardedRef, ReactElement} from 'react';
import React, {forwardRef, useCallback, useEffect, useMemo, useRef} from 'react';
import type {View} from 'react-native';
// We take ScrollView from this package to properly handle the scrolling of AutoCompleteSuggestions in chats since one scroll is nested inside another
import {ScrollView} from 'react-native-gesture-handler';
import Animated, {Easing, FadeOutDown, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
Expand Down
2 changes: 1 addition & 1 deletion src/components/AutoCompleteSuggestions/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {ReactElement} from 'react';
import type {ReactElement} from 'react';

type MeasureParentContainerCallback = (x: number, y: number, width: number) => void;

Expand Down
5 changes: 3 additions & 2 deletions src/components/AutoUpdateTime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
import DateUtils from '@libs/DateUtils';
import {Timezone} from '@src/types/onyx/PersonalDetails';
import type {Timezone} from '@src/types/onyx/PersonalDetails';
import Text from './Text';
import withLocalize, {WithLocalizeProps} from './withLocalize';
import type {WithLocalizeProps} from './withLocalize';
import withLocalize from './withLocalize';

type AutoUpdateTimeProps = WithLocalizeProps & {
/** Timezone of the user from their personal details */
Expand Down
7 changes: 4 additions & 3 deletions src/components/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import React, {useEffect, useState} from 'react';
import {StyleProp, View, ViewStyle} from 'react-native';
import type {StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import useNetwork from '@hooks/useNetwork';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ReportUtils from '@libs/ReportUtils';
import {AvatarSource} from '@libs/UserUtils';
import type {AvatarSource} from '@libs/UserUtils';
import type {AvatarSizeName} from '@styles/utils';
import CONST from '@src/CONST';
import {AvatarType} from '@src/types/onyx/OnyxCommon';
import type {AvatarType} from '@src/types/onyx/OnyxCommon';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
import Image from './Image';
Expand Down
7 changes: 4 additions & 3 deletions src/components/AvatarWithDisplayName.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, {useCallback, useEffect, useRef} from 'react';
import {View} from 'react-native';
import {OnyxCollection, OnyxEntry, withOnyx} from 'react-native-onyx';
import {ValueOf} from 'type-fest';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
Expand All @@ -11,7 +12,7 @@ import * as ReportUtils from '@libs/ReportUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import {PersonalDetails, Policy, Report, ReportActions} from '@src/types/onyx';
import type {PersonalDetails, Policy, Report, ReportActions} from '@src/types/onyx';
import DisplayNames from './DisplayNames';
import MultipleAvatars from './MultipleAvatars';
import ParentNavigationSubtitle from './ParentNavigationSubtitle';
Expand Down
3 changes: 2 additions & 1 deletion src/components/Badge.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, {useCallback} from 'react';
import {GestureResponderEvent, PressableStateCallbackType, StyleProp, TextStyle, View, ViewStyle} from 'react-native';
import type {GestureResponderEvent, PressableStateCallbackType, StyleProp, TextStyle, ViewStyle} from 'react-native';
import {View} from 'react-native';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
Expand Down
3 changes: 2 additions & 1 deletion src/components/Banner.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, {memo} from 'react';
import {StyleProp, TextStyle, View, ViewStyle} from 'react-native';
import type {StyleProp, TextStyle, ViewStyle} from 'react-native';
import {View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
Expand Down
6 changes: 4 additions & 2 deletions src/components/BaseMiniContextMenuItem.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, {ForwardedRef} from 'react';
import {PressableStateCallbackType, View} from 'react-native';
import type {ForwardedRef} from 'react';
import React from 'react';
import type {PressableStateCallbackType} from 'react-native';
import {View} from 'react-native';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import DomUtils from '@libs/DomUtils';
Expand Down
7 changes: 4 additions & 3 deletions src/components/BlockingViews/BlockingView.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import {ImageSourcePropType, View} from 'react-native';
import {SvgProps} from 'react-native-svg';
import type {ImageSourcePropType} from 'react-native';
import {View} from 'react-native';
import type {SvgProps} from 'react-native-svg';
import AutoEmailLink from '@components/AutoEmailLink';
import Icon from '@components/Icon';
import Text from '@components/Text';
Expand All @@ -9,7 +10,7 @@ import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import variables from '@styles/variables';
import {TranslationPaths} from '@src/languages/types';
import type {TranslationPaths} from '@src/languages/types';

type BlockingViewProps = {
/** Expensicon for the page */
Expand Down
2 changes: 1 addition & 1 deletion src/components/BlockingViews/FullPageNotFoundView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import variables from '@styles/variables';
import {TranslationPaths} from '@src/languages/types';
import type {TranslationPaths} from '@src/languages/types';
import ROUTES from '@src/ROUTES';
import BlockingView from './BlockingView';

Expand Down
Loading
Loading