Skip to content

TypeScript Guidelines#21050

Closed
fabioh8010 wants to merge 11 commits into
Expensify:mainfrom
fabioh8010:feature/typescript-guidelines
Closed

TypeScript Guidelines#21050
fabioh8010 wants to merge 11 commits into
Expensify:mainfrom
fabioh8010:feature/typescript-guidelines

Conversation

@fabioh8010

Copy link
Copy Markdown
Contributor

Details

This PR aims to establish the TypeScript guidelines that are going to be used in the project.

Fixed Issues

$ #20623
PROPOSAL: -

Tests

Not applicable.

Offline tests

Not applicable.

QA Steps

Not applicable.

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android / native
    • Android / Chrome
    • iOS / native
    • iOS / Safari
    • MacOS / Chrome / Safari
    • MacOS / Desktop
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that the left part of a conditional rendering a React component is a boolean and NOT a string, e.g. myBool && <MyComponent />.
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product is localized by adding it to src/languages/* files and using the translation method
      • If any non-english text was added/modified, I verified the translation was requested/reviewed in #expensify-open-source and it was approved by an internal Expensify engineer. Link to Slack message:
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is approved by marketing by adding the Waiting for Copy label for a copy review on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG))
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.
  • I have checked off every checkbox in the PR author checklist, including those that don't apply to this PR.

Screenshots/Videos

Not applicable.

@fabioh8010 fabioh8010 requested a review from a team as a code owner June 19, 2023 16:09
@melvin-bot melvin-bot Bot requested review from allroundexperts and removed request for a team June 19, 2023 16:09
@melvin-bot

melvin-bot Bot commented Jun 19, 2023

Copy link
Copy Markdown

@allroundexperts Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@neil-marcellini neil-marcellini left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looking good so far. I added some comments and I'll review the rest tomorrow.

Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md

@mrousavy mrousavy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

looks good overall!

Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md
Comment thread contributingGuides/TYPESCRIPT_STYLE.md
Comment thread contributingGuides/TYPESCRIPT_STYLE.md
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
@fabioh8010

Copy link
Copy Markdown
Contributor Author

Comments addressed!

Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated

@neil-marcellini neil-marcellini left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looking good, I've reviewed all of it now. I have a few small changes I would like to see and a handful of questions. I'm really glad we have you guys working on this, I've learned so much already!

Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
}
```

The `any` type allows assignment to all types and dereference of any property, which is undesirable and should be avoided. Instead, and in most cases, use the `unknown` type which expresses a similar concept and is much safer as it requires narrowing the type before using it. When you know that the type structure is a object but you don't have context about the content, use `Record<string, unknown>`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The "string" key can also be a number or a symbol right? We have a lot of objects keyed by integers. I was wondering about that and found the answer on Reddit, but I would love to have a link to a more official source.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I didn't know about this one until now to be honest 😅 . I guess we have the explanation here and here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for finding it in the docs! NAB: Maybe add a sentence to clarify that the Record key can also be a number or a symbol when it's written like this, and link to the TS docs.

@blazejkustra blazejkustra Jun 22, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Added clarification to this section @neil-marcellini @fabioh8010.

Maybe add a sentence to clarify that the Record key can also be a number or a symbol

Turns out that Record<string, T> allows numeric keys while not permitting symbol keys: TS playground

Comment thread contributingGuides/TYPESCRIPT_STYLE.md
@hayata-suenaga

Copy link
Copy Markdown
Contributor

still reviewing...

@fabioh8010

Copy link
Copy Markdown
Contributor Author

Addressed more comments!

@neil-marcellini neil-marcellini left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm happy with it! Thanks.

@hayata-suenaga

Copy link
Copy Markdown
Contributor

was reviwng TS declaration PR for expensify-common

still reviewing the guideline

@hayata-suenaga

Copy link
Copy Markdown
Contributor

still reviewing... I have several items I think we should add to the guideline, but I don't have time to get down on them right now
I'll finish review this weekend 🙇

Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated

### Platform-Specific Variants

In most cases, the code written for this repo should be platform-independent. In such cases, each module should have a single file, `index.ts`, which defines the module's exports. There are, however, some cases in which a feature is intrinsically tied to the underlying platform. In such cases, the following file extensions can be used to export platform-specific code from a module:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's de-dupe a bit by replace lines 23-31 with the following:

Platform-specific TypeScript files follow the same naming conventions as [JavaScript](https://github.com/Expensify/App#platform-specific-file-extensions) files, except with the `.ts`/`.tsx` extension instead of `.js`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Done 👍

export type { ComponentProps, Helpers };
```

In case there is no default implementation, you have to create a `index.d.ts` declaration file. When importing the module from other files, TypeScript will automatically pick up the type definitions from `index.d.ts`. Be careful when defining `index.d.ts` as declaration files aren't checked by the TypeScript compiler (with `skipLibCheck: true` - [source](https://www.typescriptlang.org/tsconfig#skipLibCheck)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Apologies if this has been asked-and-answered, but I wonder if, instead of creating two patterns where one of them causes us to lose type-safety, we should instead enforce that every module has a default implementation (i.e: if you have index.desktop.js, index.android.js, and index.ios.js, then we would have index.js and know that it must be for web)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Here is a long discussion in TS config doc regarding this topic.

TL;DR:
We extracted two types of platform specific variants:

  • With default implementation, only when it makes sense to have it (usually applies to more than one platform)
  • No default implementation, each platform is different (like in getPlatform utility - example).

@fabioh8010 comment on platform specific variants without default implementation 👇

I think we don't have better options than using .d.ts files for this kind of situation. The getPlatform is a perfect example why I need this type of file because we don't have index.ts for it. An possible alternative would be to choose one of the platform-specific files and rename it to index.ts, but this is terrible in my opinion. Using .d.ts will be very beneficial for us.

@blazejkustra blazejkustra Jun 23, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

instead of creating two patterns where one of them causes us to lose type-safety, we should instead enforce that every module has a default implementation

We don't really lose type-safety with declaration files as long as the name of the function is the same and types are shared from types.ts file and used in all implementations.

if you have index.desktop.js, index.android.js, and index.ios.js, then we would have index.js and know that it must be for web

I have two problems with this approach:

  1. It doesn't really eliminate type-safety problem that you mentioned with declaration files. What if each platform has a return type of literal strings: 'desktop' for desktop, 'ios' for iOS, 'android' for Android and 'web' for web. Rename index.web.ts to index.ts, now the return type of default implementation is 'web'. When importing, type 'web' is inferred, but it should be either string or 'ios' | 'android' | 'desktop' | 'web' in reality. That's why we came up with types.ts file which exports shared types (Props, return types and whatever is needed) to default and platform specific implementations.
  2. It is less readable in my opinion. In order to know what is the purpose of default implementation, we have to check all remaining files to check what platform uses it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If I were to standardize one approach, I would opt for always requiring implementations for each of the four platforms and including an index.d.ts file with a common interface + types.ts with shared types.

The approach described in the doc is also fine with me 👍

@hayata-suenaga hayata-suenaga Jun 23, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I agree with @blazejkustra that the approach described in the current guideline looks fine.

But if we really want to standardize it, I'll opt for the approach @gedu mentioned: always use index.ts for only export purposes.

// Button.types.ts (types)
export type ButtonProps = {...};

// Button.ios.ts (platform-specific implementation)
import  { ButtonProps } from "./Button.types";
export default function Button({...}: ButtonProps) {...};

// Button.ts (default implementation)
import  { ButtonProps } from "./Button.types";
export default function Button({...}: ButtonProps) {...};

// index.ts (exports)
export { default } from "./Button";
export * from "./Button.types";

This approach is what I see in component libraries and is what I believe is the standard.

What do you think?
cc: @fabioh8010 @blazejkustra

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

But if we really want to standardize it, I'll opt for the approach @gedu mentioned: always use index.ts for only export purposes.

What if there is no default implementation? Do we need to implement Button.d.ts file then? I think files structure is less readable with this approach.

This approach is what I see in component libraries and is what I believe is the standard.

Could you provide an example? @hayata-suenaga

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

index.ts is usually used to export stuff in component libraries

Those libraries are natively typed and also have Button.types.ts for Button component, for example.

However...

What if there is no default implementation? Do we need to implement Button.d.ts file then?

I tested this, and no, it didn't work without default implementation (without Button.ts) 😢 and creating Buttno.d.ts and index.ts seems too much.

So let's go with our original plan 👍


Note that `index.ts` should contain the default implementation, and only platform-specific implementations should be done in their respective files. i.e: If you have mobile-specific implementation in `index.native.ts`, then the desktop/web implementation can be contained in a shared `index.ts`.

For each platform-specific module, create shared type definitions in a separate `types.ts` file and place it in the same folder. `types.ts` has to export shared types which are **compatible with all platform-specific implementations**. Declare component props, return types, and other common types in this file.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Will tsc validate that each platform-specific implementation is compatible with the shared types? i.e: it needs to check types not only for index.ts, but also index.native.ts. Will tsc handle that for us?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Will tsc validate that each platform-specific implementation is compatible with the shared types?

Shared types (types defined in types.ts) will be used in each variant. Each TS file is checked individually, meaning that if a shared type isn't compatible with implementation we will get an error (error will be showed in platform specific implementations, not in types.ts file).

Will tsc validate that each platform-specific implementation is compatible with other platform specific implementations?

No, from TS perspective all these files are separate modules that aren't connected, that means there might be a case where index.ios.ts exports function named 'x' and index.android.ts exports function named 'xx' and tsc won't complain.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No, from TS perspective all these files are separate modules that aren't connected, that means there might be a case where index.ios.ts exports function named 'x' and index.android.ts exports function named 'xx' and tsc won't complain.

@roryabraham This is the reason we have to define shared types in types.ts and make sure you export them in each platform-specific file.


Refer to the React Native documentation for more information about [Platform Specific Code](https://reactnative.dev/docs/platform-specific-code).

## Naming Conventions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It seems like most of this might be possible to enforce via https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/naming-convention.md, maybe we can implement that in #20179 and then remove this section. In general I don't think the guidelines would be needed if they were enforced by lint rules.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this eslint rule is a nice addition to this section. Even with the rule I would leave this part in the guideline.

It seems like most of this might be possible to enforce via link

I don't think it is possible to enforce CONSTANT_CASE to only work for global-level constant values. Other should be fine I believe 👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I never used this rule before. I'm not sure if we need it as I haven't seen any naming convention discrepancies in the codebase but it is nice addition and I can add it to the Typescript setup PR!

@hayata-suenaga @fabioh8010 thoughts?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@roryabraham, great find! @blazejkustra do you think you can figure out options for this rule to make the rule conform to our naming standard?

I think it's worth writing down even short sentences in the guideline even lint rules enforce some aspects of the guideline.

otherwise, we also don't need section for Enum or any or the usage of @ts-ignore

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@blazejkustra I think you last commit about the eslint rule solve this discussion as well, isn't?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I tried and came up with this rule (added it to the Typescript setup PR):

'@typescript-eslint/naming-convention': [
  'error',
  {
      selector: 'variable',
      format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
  },
  {
      selector: ['property', 'function'],
      format: ['camelCase', 'PascalCase'],
  },
  {
      selector: ['enumMember', 'typeLike', 'typeParameter'],
      format: ['PascalCase'],
  },
  {
      selector: ['parameter', 'method'],
      format: ['camelCase'],
  },
],

Explanations:

  • variable: camelCase for basic variables, UPPER_CASE for global-level constants, PascalCase for React Components (we need it when using forwardRef)
  • property, function: 'camelCase' for default functions and object properties. PascalCase for react components and Enum like object properties.
  • enumMember, typeLike, typeParameter: PascalCase for all types.
  • parameter, method: camelCase for class methods and function parameters

This rule doesn't fully conform with our naming standards:

  • Global-level constants can be written with camelCase or PascalCase.
  • Object properties can be written in PascalCase.
  • Functions (not components) can be written in PascalCase.

There are more inconsistencies like this due to the fact that some selectors are used in multiple contexts. It doesn't fully conform with our naming standards, it's still helpful and eliminates some of the mistakes that could happen.

Thoughts @fabioh8010 @hayata-suenaga @roryabraham?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

LGTM! In this case, we should keep the conventions in the guideline as it's not possible to cover all cases with the rule.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should put non-typescript naming conventions inside our existing JavaScript style guidelines.

I know linting of the naming convention was made possible because we introduced TS eslint plugin, but let's keep JavaScript stuff in STYLE.md instead of TypeScript guideline

Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated
Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated

The `any` type allows assignment to all types and dereference of any property, which is undesirable and should be avoided. Instead, and in most cases, use the `unknown` type which expresses a similar concept and is much safer as it requires narrowing the type before using it.

When you know that the type structure is a object but you don't have context about the content, use `Record<string, unknown>`. Note that numeric keys are allowed with the `Record<string, unknown>` type. Numeric keys are implicitly converted to strings during property assignment and access. To read more about this, see [TypeScript documentation](https://www.typescriptlang.org/docs/handbook/2/objects.html#dynamically-adding-properties).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
When you know that the type structure is a object but you don't have context about the content, use `Record<string, unknown>`. Note that numeric keys are allowed with the `Record<string, unknown>` type. Numeric keys are implicitly converted to strings during property assignment and access. To read more about this, see [TypeScript documentation](https://www.typescriptlang.org/docs/handbook/2/objects.html#dynamically-adding-properties).
When you know that the type structure is an object, but you don't have context about the content, use `Record<string, unknown>`. Note that numeric keys are allowed with the `Record<string, unknown>` type. Numeric keys are implicitly converted to strings during property assignment and access. To read more about this, see [TypeScript documentation](https://www.typescriptlang.org/docs/handbook/2/objects.html#dynamically-adding-properties).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I also think it might be good to include an example showing Record<string, unknown>, because that's not included in the example that follows.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Changed the text and added an example.

Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated

To achieve better and safer typing of components with default prop values, all usages `defaultProps` shall be removed and replaced by prop destructuring. Please head to **Typing components** section to understand how you can convert your implementation.

## JSDoc annotations

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we also enforce this with a lint rule?

@blazejkustra blazejkustra Jun 23, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I found this rule to disallow types on @returns and @param tags - link. I did not find a rule to completely ban params and returns 😒

@hayata-suenaga @fabioh8010 wdyt?

@hayata-suenaga hayata-suenaga Jun 23, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@blazejkustra thank you for the research 🙌
Let's use the plugin and specify the rule for not using type annotations 👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Done! cc @fabioh8010

Comment thread contributingGuides/TYPESCRIPT_STYLE.md
Comment thread contributingGuides/TYPESCRIPT_STYLE.md
@blazejkustra blazejkustra mentioned this pull request Jun 23, 2023
57 tasks
@hayata-suenaga hayata-suenaga removed the request for review from allroundexperts June 23, 2023 22:46
@hayata-suenaga

hayata-suenaga commented Jun 26, 2023

Copy link
Copy Markdown
Contributor

I reviewed the guideline over the weekend. I like the guideline and great job @fabioh8010 🍾

I just wanted to organize the guideline more and wanted to make it way more concise. So I created another PR based off this guideline PR. You can also see how the markdown files look like in this repo I created. Please review that one when you have time this week. (I'm still finalizing details and still doing proofreading for grammatical and spelling mistakes).

In the new PR, I split this guideline into two files (one contains rules that must be followed and the other one contains guides on how to accomplish certain things in TypeScript (ex. typing forwardRef). and of course, I added a dedicated file just for the propTypes to TS conversion table that @fabioh8010 created for us

Most of the stuff is just copy and past from this PP, but I drastically cut off guidelines that are already covered by ESLint rules. For those rules that errors if not followed by ESLint, I made the wording more strict ex. "Do not use any".

Also, I added instructions on what to do when rules in guidelines cannot be followed for certain situation (posting comments on #expensify-open-source Slack channel as discussed on the design doc.

@fabioh8010 @blazejkustra @mrousavy I couldn't add you to the new PR as reviewers. Let we know if you cannot access it.

Comment thread contributingGuides/TYPESCRIPT_STYLE.md Outdated

// index.website.ts
export default function getPlatform(): Platform {
return "website";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Change to 'web'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

fyi: Adjusted 👌


Refer to the React Native documentation for more information about [Platform Specific Code](https://reactnative.dev/docs/platform-specific-code).

## Naming Conventions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I tried and came up with this rule (added it to the Typescript setup PR):

'@typescript-eslint/naming-convention': [
  'error',
  {
      selector: 'variable',
      format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
  },
  {
      selector: ['property', 'function'],
      format: ['camelCase', 'PascalCase'],
  },
  {
      selector: ['enumMember', 'typeLike', 'typeParameter'],
      format: ['PascalCase'],
  },
  {
      selector: ['parameter', 'method'],
      format: ['camelCase'],
  },
],

Explanations:

  • variable: camelCase for basic variables, UPPER_CASE for global-level constants, PascalCase for React Components (we need it when using forwardRef)
  • property, function: 'camelCase' for default functions and object properties. PascalCase for react components and Enum like object properties.
  • enumMember, typeLike, typeParameter: PascalCase for all types.
  • parameter, method: camelCase for class methods and function parameters

This rule doesn't fully conform with our naming standards:

  • Global-level constants can be written with camelCase or PascalCase.
  • Object properties can be written in PascalCase.
  • Functions (not components) can be written in PascalCase.

There are more inconsistencies like this due to the fact that some selectors are used in multiple contexts. It doesn't fully conform with our naming standards, it's still helpful and eliminates some of the mistakes that could happen.

Thoughts @fabioh8010 @hayata-suenaga @roryabraham?

@hayata-suenaga

Copy link
Copy Markdown
Contributor

Closing this one in favor of the new PR 🙇

We can keep existing unresolved conversations happening here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants