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

chore: Updated Typescript types. #11193

Closed
wants to merge 15 commits into from
Closed

chore: Updated Typescript types. #11193

wants to merge 15 commits into from

Conversation

yaldram
Copy link
Contributor

@yaldram yaldram commented Feb 16, 2022

Description

  • Update typescript to 4.2.4 & fix typescript types.

Fixes #11091
Fixes #9134

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

  • Run yarn start the code should compile without any typescript errors in the console.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

@vercel
Copy link

vercel bot commented Feb 16, 2022

This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: https://vercel.com/get-appsmith/appsmith/GhTvujQpg7ffVjTSGSkPHNv6j5KB
✅ Preview: https://appsmith-git-typescript-upgrade-get-appsmith.vercel.app

@yaldram yaldram changed the title Updated Typescript types. Task: Updated Typescript types. Feb 16, 2022
@yaldram yaldram requested a review from arunvjn February 21, 2022 13:57
@yaldram yaldram changed the title Task: Updated Typescript types. chore: Updated Typescript types. Feb 21, 2022

function* createApplication() {
const userOrgs: Organization[] = yield select(getOnboardingOrganisations);
const currentUser = yield select(getCurrentUser);
const currentUser: User | undefined = yield select(getCurrentUser);
Copy link
Contributor

@arunvjn arunvjn Feb 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For such selectors or function where we know that return types cannot be undefined/null, we should wrap the return values of these functions/selectors with a function like below and reduce type.

function shouldBeDefined<T>(argument: T | undefined | null, message: string = 'some error message to default to'): T {
  if (argument === undefined || argument === null) {
    throw new TypeError(message);
  }

  return argument;
}

Here the return value of the getCurrentUser can be wrapped with shouldBeDefined to get the type we expect.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your suggestions. As discussed yesterday we have many selectors that use .find() and therefore return T | undefined. We can either wrap all our selectors with the above function or we can improve the types like making the actionId of type keyof typeof state.actions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yaldram That might not work since the return type of .find() itself, is T | undefined.
Also, we should only wrap the return where we are sure that it cannot be undefined.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arunvjn Thanks, yes we need to implement shouldbeDefined like solution. @riodeuno

@@ -161,8 +164,11 @@ export function* closeModalSaga(
let widgetIds: string[] = [];
// If modalName is provided, we just want to close this modal
if (modalName) {
const widget = yield select(getWidgetByName, modalName);
widgetIds = [widget.widgetId];
const widget: FlattenedWidgetProps | undefined = yield select(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should write and use a utility type here like type MayNotExist<T> = T | undefined;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks make sense, we will first try to handle the T | undefined cases in the selectors if a selector is returning T | undefined I will replace all the above type with type MayNotExist<T> = T | undefined;

@@ -73,10 +76,16 @@ function* fetchPluginFormConfigsSaga() {
pluginIdFormsToFetch.add(apiPlugin.id);
}
for (const id of pluginIdFormsToFetch) {
pluginFormRequests.push(yield call(PluginsApi.fetchFormConfig, id));
const response: GenericApiResponse<PluginFormPayload> = yield call(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to standardize the names for request and response types. This needs to done across the code base. Naming convention needs to be discussed and decided. I vote for ${api}RequestData and ${api}ResponseData. So in this case, the types would be FetchFormConfigRequestData and FetchFormConfigResponseData

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion. @riodeuno please provide your inputs.

@@ -142,7 +142,7 @@ export const getCurrentPageName = createSelector(
getPageListState,
(pageList: PageListReduxState) =>
pageList.pages.find((page) => page.pageId === pageList.currentPageId)
?.pageName,
?.pageName || "PAGE_NAME_NOT_FOUND",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throw error here when pageName is not found. Makes sure the selector always return string. Refer
https://github.com/appsmithorg/appsmith/pull/11193/files#r811332675

@yaldram
Copy link
Contributor Author

yaldram commented Feb 23, 2022

@arunvjn @riodeuno I am leaving behind some of my findings : -

  1. I have implemented shouldBeDefined locally thanks to @arunvjn's suggestions a lot of the type issues were resolved.
  2. But there are some types in our code which do return undefined and we have handled them in places and we have some type mismatches also, for now I have @ts-expect-error but we can resolve these issues by talking to the authors.
  3. We should implement type MayNotExist<T> at many places in our code we have T | undefined (across 158 files).
  4. We should also type the unknown Api Responses if possible.

@yaldram
Copy link
Contributor Author

yaldram commented Feb 24, 2022

Got some answers for typing redux-saga - redux-saga/redux-saga#2251

@@ -35,10 +35,10 @@ export interface QueryConfig {
queryString: string;
}

export interface ActionCreateUpdateResponse extends ApiResponse {
export type ActionCreateUpdateResponse = ApiResponse & {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yaldram Could you explain why we have changed interface to type here?
The reason for asking, is that if we have a standard reason for deciding one over the other, we can make it a practice in all of our codebase.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually we have our ApiResponse as a generic type here, before it was an interface so I have changed some interfaces to types like : -

type TemplateResponse = ApiResponse<Template>

and if you want to add more params to the type just extend it using & like -

type JSCollectionCreateUpdateResponse = ApiResponse & { id: string } 

Copy link
Contributor

@arunvjn arunvjn Mar 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All API responses are of the type ApiResponse. We are doing something wrong here.
Did you mean to do something like ApiResponse<Template & { id: string }> ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @arunvjn no actually this id field of type string is an additional field on the response and not part of the data field.

request: ActionApiResponseReq;
errorType?: string;
};
export type ActionExecutionResponse = ApiResponse<{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering if this could be achieved by using interface extends ApiResponse.
@yaldram I believe we had discussed the advantage of this approach. Could you mention it here so that we can consider this consistently further into the future.

Copy link
Contributor Author

@yaldram yaldram Mar 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a discussion with @arunvjn where he suggested to use ApiResponse<T> as a Generic type we had a GenericApiResponse previously but it had the same fields as ApiResponse with an additional field that can be undefined. We are now replacing all our extends ApiResponse to use the generic version of it.

old version : -

export type ApiResponse = {
  responseMeta: ResponseMeta;
  data: any;
  code?: string;
};

export interface ActionExecutionResponse {
  responseMeta: ResponseMeta;
  data: {
    body: Record<string, unknown> | string;
    headers: Record<string, string[]>;
    statusCode: string;
    isExecutionSuccess: boolean;
    request: ActionApiResponseReq;
    errorType?: string;
  };
  clientMeta: {
    duration: string;
    size: string;
  };
}

now : -

export type ApiResponse<T = unknown> = {
  responseMeta: ResponseMeta;
  data: T;
  code?: string;
};

export type ActionExecutionResponse = ApiResponse<{
  body: Record<string, unknown> | string;
  headers: Record<string, string[]>;
  statusCode: string;
  isExecutionSuccess: boolean;
  request: ActionApiResponseReq;
  errorType?: string;
}> & {
  clientMeta: {
    duration: string;
    size: string;
  };
};

import { Variable, JSAction } from "entities/JSCollection";
import { PluginType } from "entities/Action";
export interface JSCollectionCreateUpdateResponse extends ApiResponse {

export type JSCollectionCreateUpdateResponse = ApiResponse & {
id: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you confirm if this is supposed to be something like ApiResponse<{id: string}> instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it should be : -

export type JSCollectionCreateUpdateResponse = ApiResponse & {
  id: string;
}

The above type has all the fields of ApiResponse, plus the id: string field.
old version : -

export interface JSCollectionCreateUpdateResponse extends ApiResponse {
  id: string;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this should be ApiResponse<{id: string}>

Copy link
Contributor Author

@yaldram yaldram Mar 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @arunvjn actually we have an additional field here id of type string which will not be part of the data field

@github-actions
Copy link

This PR has not seen activitiy for a while. It will be closed in 7 days unless further activity is detected.

@github-actions github-actions bot added Enhancement New feature or request Frontend This label marks the issue or pull request to reference client code Task A simple Todo UI Building Product Issues related to the UI Building experience UI Building Pod labels Mar 30, 2022
@vercel
Copy link

vercel bot commented Apr 21, 2022

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated
appsmith ✅ Ready (Inspect) Visit Preview Apr 21, 2022 at 10:55AM (UTC)

@riodeuno
Copy link
Contributor

riodeuno commented May 5, 2022

@yaldram Shall we close this without deleting the branch in favour of #13195?

@yaldram
Copy link
Contributor Author

yaldram commented May 5, 2022

@yaldram Shall we close this without deleting the branch in favour of #13195?

Sure we can.

@riodeuno
Copy link
Contributor

riodeuno commented May 5, 2022

Closing in favour of #13195. DONOT delete branch. @yaldram must be consulted before attempting to do so.

@riodeuno riodeuno closed this May 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement New feature or request Frontend This label marks the issue or pull request to reference client code Stale Task A simple Todo UI Building Product Issues related to the UI Building experience
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Task]: Update Typescript to 4.2.4 [Task]: Improve Typescript support for Sagas
3 participants