Skip to content

TypeScript: Migrate packages/list-reusable-blocks package to TypeScript - #70518

Merged
manzoorwanijk merged 14 commits into
WordPress:trunkfrom
Infinite-Null:convert/list-reusable-blocks-to-typescript
Apr 13, 2026
Merged

manzoorwanijk merged 14 commits into
WordPress:trunkfrom
Infinite-Null:convert/list-reusable-blocks-to-typescript

Conversation

@Infinite-Null

@Infinite-Null Infinite-Null commented Jun 25, 2025 •

Copy link
Copy Markdown
Contributor

What?

Part of: #67691
Migrating the packages/list-reusable-blocks package to TypeScript.

Why?

Type safety.

Testing Instructions

Typecheck and unit tests.

@t-hamano t-hamano mentioned this pull request Jun 24, 2025
47 of 50 tasks
@t-hamano t-hamano added the [Type] Code Quality Issues or PRs that relate to code quality label Jun 25, 2025
@Infinite-Null
Infinite-Null marked this pull request as ready for review June 26, 2025 14:22
@github-actions

github-actions Bot commented Jun 26, 2025 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: Infinite-Null <ankitkumarshah@git.wordpress.org>
Co-authored-by: manzoorwanijk <manzoorwanijk@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@Infinite-Null Infinite-Null changed the title [WIP] Migrate packages/list-reusable-blocks package to TypeScript TypeScript: Migrate packages/list-reusable-blocks package to TypeScript Jun 26, 2025
@manzoorwanijk

Copy link
Copy Markdown
Member

Can we update this branch from trunk and then we can review it?

Copilot AI 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.

Pull request overview

This PR migrates the packages/list-reusable-blocks package from JavaScript to TypeScript, adding type annotations and creating type definitions to improve type safety.

Changes:

  • Added TypeScript configuration files for the package and root project
  • Created type definitions for components and data structures
  • Migrated all source files (.js to .tsx/.ts) with type annotations

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tsconfig.json Added reference to the list-reusable-blocks package
packages/list-reusable-blocks/tsconfig.json Created TypeScript configuration for the package
packages/list-reusable-blocks/src/utils/types.ts Added type definitions for components and data structures
packages/list-reusable-blocks/src/utils/import.ts Migrated to TypeScript with type annotations
packages/list-reusable-blocks/src/utils/file.ts Migrated to TypeScript with type annotations
packages/list-reusable-blocks/src/utils/export.ts Migrated to TypeScript with type annotations
packages/list-reusable-blocks/src/index.tsx Migrated to TypeScript with proper event typing
packages/list-reusable-blocks/src/components/import-form/index.tsx Migrated to TypeScript with React event types
packages/list-reusable-blocks/src/components/import-dropdown/index.tsx Migrated to TypeScript with proper prop typing
Comments suppressed due to low confidence (2)

packages/list-reusable-blocks/src/components/import-form/index.tsx:35

  • Using any type for the parameter defeats the purpose of TypeScript migration. The parameter should be typed as ReusableBlock based on the return type of importReusableBlock.
    packages/list-reusable-blocks/src/components/import-form/index.tsx:43
  • Using any type for the errors parameter defeats the purpose of TypeScript migration. Consider defining a proper error type or using Error type.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

raw: string;
};
wp_pattern_sync_status: string;
[ key: string ]: any;

Copilot AI Jan 11, 2026

Copy link

Choose a reason for hiding this comment

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

Using index signatures [ key: string ]: any weakens type safety. Consider defining specific properties or using a more restrictive approach if these extra properties are truly needed.

Copilot uses AI. Check for mistakes.
title: string;
content: string;
syncStatus?: string;
[ key: string ]: any;

Copilot AI Jan 11, 2026

Copy link

Choose a reason for hiding this comment

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

Using index signatures [ key: string ]: any weakens type safety. Consider defining specific properties or using a more restrictive approach if these extra properties are truly needed.

Suggested change
[ key: string ]: any;
[ key: string ]: unknown;

Copilot uses AI. Check for mistakes.
rendered: string;
};
status: string;
[ key: string ]: any;

Copilot AI Jan 11, 2026

Copy link

Choose a reason for hiding this comment

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

Using index signatures [ key: string ]: any weakens type safety. Consider defining specific properties or using a more restrictive approach if these extra properties are truly needed.

Suggested change
[ key: string ]: any;
[ key: string ]: unknown;

Copilot uses AI. Check for mistakes.
Comment on lines +2 to +7
onUpload: ( data: any ) => void;
}

interface ImportFormProps {
instanceId: string | number;
onUpload: ( reusableBlock: any ) => void;

Copilot AI Jan 11, 2026

Copy link

Choose a reason for hiding this comment

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

The type definitions use any type which defeats the purpose of TypeScript migration. The onUpload callback should have a properly typed parameter instead of any.

Suggested change
onUpload: ( data: any ) => void;
}
interface ImportFormProps {
instanceId: string | number;
onUpload: ( reusableBlock: any ) => void;
onUpload: ( data: ReusableBlockData ) => void;
}
interface ImportFormProps {
instanceId: string | number;
onUpload: ( reusableBlock: ReusableBlockData ) => void;

Copilot uses AI. Check for mistakes.
Comment on lines +2 to +7
onUpload: ( data: any ) => void;
}

interface ImportFormProps {
instanceId: string | number;
onUpload: ( reusableBlock: any ) => void;

Copilot AI Jan 11, 2026

Copy link

Choose a reason for hiding this comment

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

The type definitions use any type which defeats the purpose of TypeScript migration. The onUpload callback should have a properly typed parameter instead of any.

Suggested change
onUpload: ( data: any ) => void;
}
interface ImportFormProps {
instanceId: string | number;
onUpload: ( reusableBlock: any ) => void;
onUpload: ( data: ReusableBlockData ) => void;
}
interface ImportFormProps {
instanceId: string | number;
onUpload: ( reusableBlock: ReusableBlockData ) => void;

Copilot uses AI. Check for mistakes.

interface PostType {
rest_base: string;
[ key: string ]: any;

Copilot AI Jan 11, 2026

Copy link

Choose a reason for hiding this comment

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

Using index signatures [ key: string ]: any weakens type safety. Consider defining specific properties or using a more restrictive approach if these extra properties are truly needed.

Copilot uses AI. Check for mistakes.
parsedContent = JSON.parse( fileContent );
} catch {
parsedContent = JSON.parse( fileContent ) as ParsedContent;
} catch ( e ) {

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.

Suggested change
} catch ( e ) {
} catch {

Comment thread packages/list-reusable-blocks/src/utils/types.ts Outdated

ghost 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.

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (5)

packages/list-reusable-blocks/src/utils/file.ts:13

  • readTextFile never rejects and doesn’t handle FileReader.onerror/abort. If the read fails (or reader.result ends up null), the returned promise can hang or resolve to a non-string value (masked by the as string cast), which can later crash JSON parsing/validation. Consider wiring onerror/onabort to reject, and only resolving when typeof reader.result === 'string' (otherwise reject with a clear error).
    packages/list-reusable-blocks/src/utils/import.ts:36
  • JSON.parse can legally return non-object values (e.g. null, a string, number, array). Since parsedContent is cast to ParsedContent, the subsequent parsedContent.__file access can throw a TypeError for inputs like "null". Add a runtime guard that parsedContent is a non-null object (and ideally not an array) before accessing properties, and treat invalid shapes as an “Invalid pattern JSON file” error.
    packages/list-reusable-blocks/src/utils/import.ts:41
  • The syncStatus validation uses a truthiness check (parsedContent.syncStatus && ...), which allows invalid non-string values like 0, null, or '' (empty string skips the type check) to bypass validation. Use an explicit undefined check (e.g. parsedContent.syncStatus !== undefined) when validating the type.
    packages/list-reusable-blocks/src/components/import-form/index.tsx:49
  • importReusableBlock is already typed to resolve ReusableBlock, but the .then callback types it as any and the .catch parameter is also any. This defeats the TypeScript migration and makes errors.message unsafe. Prefer keeping the inferred ReusableBlock type in .then, and type the caught value as unknown and narrow to Error (or a custom shape) before reading .message.
    packages/list-reusable-blocks/src/index.tsx:26
  • The click handler assumes event.target is an HTMLElement and immediately reads classList/dataset. MouseEvent.target can be a non-HTMLElement (or even null), which would throw at runtime despite the cast. Add an instanceof HTMLElement (or Element) guard before accessing classList/dataset, and consider validating Number(blockId) is finite before calling exportReusableBlock to avoid /NaN requests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Infinite-Null

ghost commented Apr 7, 2026

Copy link
Copy Markdown
Contributor Author

Hi @manzoorwanijk,

Thanks for the clarification and for pointing that out!

I’ll revisit this and switch to using the existing Post and PostType types from core-data, instead of redefining them.

Could you please take another look and re-review the PR when you get a chance? Thanks again for the helpful feedback

ghost 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.

This looks much better now. Let us clean things up a bit and also update this branch from trunk.

Comment thread packages/list-reusable-blocks/src/utils/types.ts Outdated
Comment thread packages/list-reusable-blocks/tsconfig.json Outdated
Comment thread tsconfig.json Outdated
Comment thread packages/list-reusable-blocks/package.json Outdated
Comment thread packages/list-reusable-blocks/src/utils/export.ts Outdated
Comment thread packages/list-reusable-blocks/src/utils/export.ts Outdated
Comment thread packages/list-reusable-blocks/src/utils/import.ts Outdated
Comment thread packages/list-reusable-blocks/src/utils/import.ts Outdated
@Infinite-Null

ghost commented Apr 8, 2026

Copy link
Copy Markdown
Contributor Author

Hi @manzoorwanijk,
I’ve addressed all the requested changes. Could you please take a moment to review the PR when you get a chance? I’d really appreciate your feedback.

ghost 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.

Thank you for making the changes. This looks pretty close but I missed something in the last review - we need to add the types field to package.json and also to the exports['.'] in it.

ghost 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.

Pull request overview

Copilot reviewed 9 out of 11 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (4)

packages/list-reusable-blocks/src/utils/file.ts:13

  • readTextFile currently never rejects and will resolve reader.result with a as string cast even though FileReader.result can be null (or non-string on other read modes). This can leave callers hanging on read errors/abort and can propagate an invalid null value. Consider wiring reader.onerror/reader.onabort to reject, and in onload verify typeof reader.result === 'string' before resolving (otherwise reject with an error).
    packages/list-reusable-blocks/src/index.tsx:18
  • The click handler casts event.target to HTMLElement and calls classList/dataset without a runtime guard. If the click originates from a non-HTMLElement target, this will throw. Prefer checking event.target instanceof HTMLElement (or using event.target as Element + 'classList' in target) before accessing classList/dataset.
    packages/list-reusable-blocks/src/index.tsx:26
  • Number( blockId ) can produce NaN (or 0 for an empty string), which then gets interpolated into the REST path in exportReusableBlock (e.g. /.../NaN?context=edit). Parse and validate the id before calling exportReusableBlock (e.g. const id = Number.parseInt(blockId, 10) and guard with Number.isFinite(id)).
    packages/list-reusable-blocks/src/utils/import.ts:36
  • After JSON.parse, the result can be null, an array, or a primitive. The validation block accesses parsedContent.__file before ensuring it’s a non-null object, which can throw a runtime error (e.g. importing a JSON file containing null). Consider parsing into unknown first and adding an typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed) guard before reading properties.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/list-reusable-blocks/src/utils/types.ts Outdated
@Infinite-Null

ghost commented Apr 13, 2026

Copy link
Copy Markdown
Contributor Author

Hi @manzoorwanijk

Thank you for pointing out the missing fields in the package.json. I really appreciate your attention. I’ve made the necessary updates to the PR.

Whenever you have a moment, could you please take another look and share your feedback?

ghost 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.

Pull request overview

Copilot reviewed 9 out of 11 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

packages/list-reusable-blocks/src/utils/import.ts:42

  • JSON.parse() can return non-object values (e.g. null, arrays, numbers). In those cases, the subsequent property access (parsedContent.__file, etc.) will throw a runtime TypeError instead of the intended "Invalid pattern JSON file" error. Consider parsing to unknown and validating that the result is a non-null plain object with string __file/title/content before accessing properties, so invalid-but-well-formed JSON is handled gracefully.
    packages/list-reusable-blocks/src/utils/file.ts:14
  • readTextFile() never rejects: if FileReader errors/aborts the Promise will remain pending, and reader.result is cast to string even though it can be null. Add reader.onerror/reader.onabort handling to reject the Promise (or resolve to a safe default) and ensure the resolved value is actually a string before returning it.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

ghost 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.

Thank you for making the changes. This looks good now.

@manzoorwanijk
manzoorwanijk merged commit 4afded1 into WordPress:trunk Apr 13, 2026
@github-actions github-actions Bot added this to the Gutenberg 23.0 milestone Apr 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Type] Code Quality Issues or PRs that relate to code quality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants