From 4dd223d22b68a4427c1e7f45fab9b780d4c6f406 Mon Sep 17 00:00:00 2001 From: smelaa Date: Wed, 10 Apr 2024 17:16:35 +0200 Subject: [PATCH 1/4] Improve routes validation --- src/ROUTES.ts | 21 +++++++++++---------- src/types/utils/AssertTypesNotEqual.ts | 10 ++++++++++ 2 files changed, 21 insertions(+), 10 deletions(-) create mode 100644 src/types/utils/AssertTypesNotEqual.ts diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 60fca9fac87b..8d7c1a813553 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1,6 +1,7 @@ -import type {IsEqual, ValueOf} from 'type-fest'; +import type {ValueOf} from 'type-fest'; import type CONST from './CONST'; import type {IOURequestType} from './libs/actions/IOU'; +import type AssertTypesNotEqual from './types/utils/AssertTypesNotEqual'; // This is a file containing constants for all the routes we want to be able to go to @@ -739,20 +740,20 @@ export default ROUTES; // eslint-disable-next-line @typescript-eslint/no-explicit-any type ExtractRouteName = TRoute extends {getRoute: (...args: any[]) => infer TRouteName} ? TRouteName : TRoute; +/** + * Represents all routes in the app as a union of literal strings. + * */ type AllRoutes = { [K in keyof typeof ROUTES]: ExtractRouteName<(typeof ROUTES)[K]>; }[keyof typeof ROUTES]; -type RouteIsPlainString = IsEqual; +type RoutesValidationError = + 'Error: RoutesValidationError implies that one or more routes defined within `ROUTES` have not correctly used `as const` in their `getRoute` function return value.'; -/** - * Represents all routes in the app as a union of literal strings. - * - * If this type resolves to `never`, it implies that one or more routes defined within `ROUTES` have not correctly used - * `as const` in their `getRoute` function return value. - */ -type Route = RouteIsPlainString extends true ? never : AllRoutes; +type RouteIsPlainString = AssertTypesNotEqual; + +// type Route = RouteIsPlainString extends true ? never : AllRoutes; type HybridAppRoute = (typeof HYBRID_APP_ROUTES)[keyof typeof HYBRID_APP_ROUTES]; -export type {Route, HybridAppRoute, AllRoutes}; +export type {RouteIsPlainString, HybridAppRoute, AllRoutes}; diff --git a/src/types/utils/AssertTypesNotEqual.ts b/src/types/utils/AssertTypesNotEqual.ts new file mode 100644 index 000000000000..35cc2ccc85d9 --- /dev/null +++ b/src/types/utils/AssertTypesNotEqual.ts @@ -0,0 +1,10 @@ +import type {IsEqual} from 'type-fest'; + +type MatchError = 'Error: Types do match'; +/** + * The 'AssertTypesNotEqual' type here enforces that `T1` and `T2` do not match. + * If `T1` or `T2` are different this type will cause a compile-time error + */ +type AssertTypesNotEqual extends false ? T1 : TMatchError, TMatchError = MatchError> = T1 & T2; + +export default AssertTypesNotEqual; From bc84b6abdde89f734f5d32f8d873a75b8460dbf8 Mon Sep 17 00:00:00 2001 From: smelaa Date: Wed, 10 Apr 2024 17:31:17 +0200 Subject: [PATCH 2/4] Routes validation improvement followup --- src/ROUTES.ts | 9 ++++----- src/components/MoneyRequestConfirmationList.tsx | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 8d7c1a813553..fe65a84beaf5 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -743,17 +743,16 @@ type ExtractRouteName = TRoute extends {getRoute: (...args: any[]) => in /** * Represents all routes in the app as a union of literal strings. * */ -type AllRoutes = { +type Route = { [K in keyof typeof ROUTES]: ExtractRouteName<(typeof ROUTES)[K]>; }[keyof typeof ROUTES]; type RoutesValidationError = 'Error: RoutesValidationError implies that one or more routes defined within `ROUTES` have not correctly used `as const` in their `getRoute` function return value.'; -type RouteIsPlainString = AssertTypesNotEqual; - -// type Route = RouteIsPlainString extends true ? never : AllRoutes; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type RouteIsPlainString = AssertTypesNotEqual; type HybridAppRoute = (typeof HYBRID_APP_ROUTES)[keyof typeof HYBRID_APP_ROUTES]; -export type {RouteIsPlainString, HybridAppRoute, AllRoutes}; +export type {Route, HybridAppRoute}; diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index d39e40179a9f..616685ffa90d 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -26,7 +26,7 @@ import * as TransactionUtils from '@libs/TransactionUtils'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {AllRoutes} from '@src/ROUTES'; +import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; @@ -122,7 +122,7 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & isReadOnly?: boolean; /** Depending on expense report or personal IOU report, respective bank account route */ - bankAccountRoute?: AllRoutes; + bankAccountRoute?: Route; /** The policyID of the request */ policyID?: string; From 728d97963ac66a14437d9e343f931054c8c365cd Mon Sep 17 00:00:00 2001 From: smelaa Date: Thu, 11 Apr 2024 12:20:48 +0200 Subject: [PATCH 3/4] Address review comments --- src/ROUTES.ts | 5 ++--- src/types/utils/AssertTypesNotEqual.ts | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index fe65a84beaf5..5af5a02cc417 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -742,13 +742,12 @@ type ExtractRouteName = TRoute extends {getRoute: (...args: any[]) => in /** * Represents all routes in the app as a union of literal strings. - * */ + */ type Route = { [K in keyof typeof ROUTES]: ExtractRouteName<(typeof ROUTES)[K]>; }[keyof typeof ROUTES]; -type RoutesValidationError = - 'Error: RoutesValidationError implies that one or more routes defined within `ROUTES` have not correctly used `as const` in their `getRoute` function return value.'; +type RoutesValidationError = 'Error: One or more routes defined within `ROUTES` have not correctly used `as const` in their `getRoute` function return value.'; // eslint-disable-next-line @typescript-eslint/no-unused-vars type RouteIsPlainString = AssertTypesNotEqual; diff --git a/src/types/utils/AssertTypesNotEqual.ts b/src/types/utils/AssertTypesNotEqual.ts index 35cc2ccc85d9..19372296746c 100644 --- a/src/types/utils/AssertTypesNotEqual.ts +++ b/src/types/utils/AssertTypesNotEqual.ts @@ -1,9 +1,10 @@ import type {IsEqual} from 'type-fest'; type MatchError = 'Error: Types do match'; + /** * The 'AssertTypesNotEqual' type here enforces that `T1` and `T2` do not match. - * If `T1` or `T2` are different this type will cause a compile-time error + * If `T1` or `T2` are different this type will cause a compile-time error. */ type AssertTypesNotEqual extends false ? T1 : TMatchError, TMatchError = MatchError> = T1 & T2; From 0becc8aa333680d33c58529423e2e1a4e1b3f834 Mon Sep 17 00:00:00 2001 From: smelaa Date: Thu, 11 Apr 2024 13:04:05 +0200 Subject: [PATCH 4/4] Update comment --- src/types/utils/AssertTypesNotEqual.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/utils/AssertTypesNotEqual.ts b/src/types/utils/AssertTypesNotEqual.ts index 19372296746c..237f54ec2921 100644 --- a/src/types/utils/AssertTypesNotEqual.ts +++ b/src/types/utils/AssertTypesNotEqual.ts @@ -4,7 +4,7 @@ type MatchError = 'Error: Types do match'; /** * The 'AssertTypesNotEqual' type here enforces that `T1` and `T2` do not match. - * If `T1` or `T2` are different this type will cause a compile-time error. + * If `T1` or `T2` are the same this type will cause a compile-time error. */ type AssertTypesNotEqual extends false ? T1 : TMatchError, TMatchError = MatchError> = T1 & T2;