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

[Fix] prop-types: Support typescript props interface extension and TSTypeAliasDeclaration #2721

Merged
merged 1 commit into from Aug 1, 2020

Conversation

hank121314
Copy link
Contributor

@hank121314 hank121314 commented Jul 21, 2020

Fixes #2654. Fixes #2719. Fixes #2703.

Now support the following typescript syntax for prop validation,
but extension only support with parser @typescript-eslint/parser since typescript-eslint-parser is deprecated.

  1. interface extension.
interface GenericProps {
  a: () => void;
}

interface ImplementationProps extends GenericProps {
  b: () => void;
}

export const Implementation: FC<ImplementationProps> = ({
  a,
  b,
}: ImplementationProps) => <div />;

  1. interface extension with ReturnType
const mapStateToProps = (state) => ({
  books: state.books,
});

interface BooksTable extends ReturnType<typeof mapStateToProps> {
  username: string;
}

const App = (props: BooksTable) => {
  props.books();
  return (
    <div>
      <span>{props.username}</span>
    </div>
  );
};

ReturnType can only accept one parameter, and the parameter should be function type, and that function should return an ObjectExpression.

  1. TSTypeAliasDeclaration and intersectionType
type User = {
  user: string,
};

type Props = User & { userId : string };

export default (props: Props) => {
  const { userId, user } = props;

  if (userId === 0) {
    return <p>userId is 0</p>;
  }

  return null;
};

Thanks you so much for your code review!

…ypescript props interface extension and TSTypeAliasDeclaration

Fixes jsx-eslint#2654. Fixes jsx-eslint#2719. Fixes jsx-eslint#2703.

Co-authored-by: Hank Chen <hank121314@gmail.com>
Co-authored-by: Jordan Harband <ljharb@gmail.com>
@hank121314 hank121314 changed the title [Fix] prop-types: Support typescript props interface extension and TSTypeAliasDeclaration WIP:[Fix] prop-types: Support typescript props interface extension and TSTypeAliasDeclaration Jul 23, 2020
@hank121314 hank121314 changed the title WIP:[Fix] prop-types: Support typescript props interface extension and TSTypeAliasDeclaration [Fix] prop-types: Support typescript props interface extension and TSTypeAliasDeclaration Jul 23, 2020
Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

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

Thanks, this seems great

*/
function isFunctionType(node) {
if (node.type) {
const regexp = new RegExp('^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$');
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const regexp = new RegExp('^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$');
const regexp = /^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$/;

new RegExp should never be used when a literal suffices.

altho this seems like it'd be clearer as a chain of ifs rather than using a regex.

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 advice!
How about using Array.includes ?

function isFunctionType(node) {
  if (node.type) {
    return ['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression'].includes(node.type);
  }
  return false;
}

Copy link
Member

Choose a reason for hiding this comment

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

That also seems like needless runtime overhead for only three choices. I'd prefer a simple if chain.

@hank121314
Copy link
Contributor Author

Hi @ljharb !
I refactor some code and use @typescript-eslint/parser instead of typescript-eslint-parser for all test case in prop-types and nounused-prop-types.
Thanks you so much for your code review and Sorry for taking your time! 😭

@ljharb
Copy link
Member

ljharb commented Jul 31, 2020

@hank121314 in general it's best if we use both - ie, duplicate the test cases - so we can be sure that things stay working as much as possible on the old TS parser.

@@ -3226,7 +3226,7 @@ ruleTester.run('no-unused-prop-types', rule, {
bar,
};
`,
parser: parsers.TYPESCRIPT_ESLINT
parser: parsers['@TYPESCRIPT_ESLINT']
Copy link
Member

Choose a reason for hiding this comment

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

specifically, all these test cases with the old TS parser should be left untouched, and duplicated, with the duplicate using the new TS parser.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK! I have added it back.
But I think we might need to deprecated old TS parser someday.
The ast tree between two parser have a big difference.
It is really hard to support both parser for their own ast tree.

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

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

Thanks!

@ljharb ljharb force-pushed the master branch 2 times, most recently from 4f27dae to ac422ad Compare July 31, 2020 07:14
@ljharb
Copy link
Member

ljharb commented Jul 31, 2020

I've rebased the branch for you; the eslint 4 + node 4 tests are failing, but I suspect that's because the new TS parser doesn't work there.

I'll finish updating the branch to handle that tomorrow, and then land it.

@hank121314
Copy link
Contributor Author

I've rebased the branch for you; the eslint 4 + node 4 tests are failing, but I suspect that's because the new TS parser doesn't work there.

I'll finish updating the branch to handle that tomorrow, and then land it.

Yes, seems that new TS parser only support eslint version greater than 5.
I am so grateful for your help! 😄

@ljharb
Copy link
Member

ljharb commented Jul 31, 2020

@hank121314 ok, i think i've addressed that problem, but I'm now seeing a few tests failing locally - one example:

          type User = {
            user: string;
          }

          type Props = User & {
          };

          export default (props: Props) => {
            const { userId, user } = props;

            if (userId === 0) {
              return <p>userId is 0</p>;
            }

            return null;
          };

is warning on both userId and user, but should only be warning on userId.

(Please delete your local branch and check it out fresh, so you can work on top of my rebase)

@hank121314
Copy link
Contributor Author

@hank121314 ok, i think i've addressed that problem, but I'm now seeing a few tests failing locally - one example:

          type User = {
            user: string;
          }

          type Props = User & {
          };

          export default (props: Props) => {
            const { userId, user } = props;

            if (userId === 0) {
              return <p>userId is 0</p>;
            }

            return null;
          };

is warning on both userId and user, but should only be warning on userId.

(Please delete your local branch and check it out fresh, so you can work on top of my rebase)

I'm sorry to say this pr doesn't support old TS parser for any of intersectionType or extension.
Since old TS parser is deprecated so I haven't gave it's support yet 😞 .
If old TS parser support is necessary, I will update this pr.

@ljharb
Copy link
Member

ljharb commented Aug 1, 2020

@hank121314 ah - so, one option is certainly to comment out the "old TS parser" tests, land this, and fix them later - but i'd prefer to do them all at once, if you're willing :-)

@hank121314
Copy link
Contributor Author

@hank121314 ah - so, one option is certainly to comment out the "old TS parser" tests, land this, and fix them later - but i'd prefer to do them all at once, if you're willing :-)

It's my pleasure! I have pushed the latest version with old TS parser support and add some test case in no-unused-prop-types.
Thank you for your support and code view once again.

@ljharb
Copy link
Member

ljharb commented Aug 1, 2020

k, figured out the test failures in eslint 4 :-) rebased again.

@sergeushenecz
Copy link

Hi. It not working if extented interface exist in another file.

@ljharb
Copy link
Member

ljharb commented Jul 10, 2023

@sergeu90 cross-file linting isn't practical.

@sergeushenecz
Copy link

sergeushenecz commented Jul 10, 2023

@ljharb If I have interface product for example and I want validate props in component product. I need move interface to the file product component right? But if I have multiply places where I want use product then I have global product interface and local product interface in the product component? And if I want to add new property I need add to 2 places?

@ljharb
Copy link
Member

ljharb commented Jul 10, 2023

Yes, that's just a limitation in linting. It's more clear though to repeat prop names so that someone doesn't have to go read another file to find out what props a component takes.

@sergeushenecz
Copy link

@ljharb Thanks for reply

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
3 participants