Improve TypeScript types and error handling

- Created proper TypeScript types for Office API operations
- Replaced 'any' casts with appropriate types
- Added type-safe utility functions in office-types.ts
- Improved error handling with proper unknown type
- Added helper functions for PowerPoint shape operations
- Created proper TypeScript interface for webpack HMR

These changes improve code quality, maintainability, and help catch potential bugs at compile time rather than runtime.

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-03-15 00:02:35 +01:00
parent 07b0232726
commit 50966867b2
4 changed files with 79 additions and 13 deletions
+3 -2
View File
@@ -2,6 +2,7 @@ import * as React from "react";
import { Button, ButtonProps } from "@fluentui/react-components";
import { useStatusContext } from "./App";
import { useCommonStyles } from "./commonStyles";
import { getErrorMessage } from "../types/office-types";
export interface ActionButtonProps {
icon: ButtonProps["icon"];
@@ -37,8 +38,8 @@ export const ActionButton: React.FC<ActionButtonProps> = ({
setIsProcessing(true);
try {
await onClick();
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
} catch (error: unknown) {
const errorMessage = getErrorMessage(error);
setStatusMessage(`Error: ${errorMessage}`);
setStatusType("error");
console.error(`Error in ${title} action:`, error);
+13 -8
View File
@@ -7,6 +7,7 @@ import {
} from "@fluentui/react-icons";
import { useStatusContext } from "./App";
import { useCommonStyles } from "./commonStyles";
import { getErrorMessage, isPictureShape, getFirstSelectedSlide, selectShapesById } from "../types/office-types";
export const RoundImage: React.FC = () => {
const styles = useCommonStyles();
@@ -38,8 +39,8 @@ export const RoundImage: React.FC = () => {
shape.load(["type"]);
await context.sync();
// Ensure the shape is a picture
if (shape.type !== PowerPoint.ShapeType.image) {
// Ensure the shape is a picture using our type-safe utility
if (!isPictureShape(shape)) {
setStatusMessage("Please select an image.");
setStatusType("warning");
return;
@@ -53,8 +54,10 @@ export const RoundImage: React.FC = () => {
const width = shape.width;
const height = shape.height;
const slide = context.presentation.getSelectedSlides().getItemAt(0);
const maskShape = slide.shapes.addGeometricShape("Ellipse" as any);
// Get the current slide using our type-safe utility
const slide = getFirstSelectedSlide(context);
// Create elliptical mask with proper type
const maskShape = slide.shapes.addGeometricShape(PowerPoint.GeometricShapeType.ellipse);
maskShape.load(["width", "height", "left", "top", "id"]);
await context.sync();
@@ -73,17 +76,19 @@ export const RoundImage: React.FC = () => {
setStatusMessage("Created mask shape. Please select both the image and the oval, then use the 'Shape Forma > Merge Shapes > Intersect' command in PowerPoint.");
setStatusType("warning");
slide.setSelectedShapes([shape.id, maskShape.id]);
// Use our type-safe utility for selecting shapes
selectShapesById(slide, [shape.id, maskShape.id]);
// Ensure we maintain the same size
shape.width = width;
shape.height = height;
await context.sync();
});
} catch (error) {
setStatusMessage(`Error: ${error.message}`);
} catch (error: unknown) {
const errorMessage = getErrorMessage(error);
setStatusMessage(`Error: ${errorMessage}`);
setStatusType("error");
console.error("Main error:", error);
console.error("Round image error:", error);
} finally {
setIsProcessing(false);
}
+15 -3
View File
@@ -19,9 +19,21 @@ Office.onReady(() => {
);
});
if ((module as any).hot) {
(module as any).hot.accept("./components/App", () => {
// Define proper module hot interface for webpack hot module replacement
interface HotModule extends NodeModule {
hot?: {
accept(path: string, callback: () => void): void;
};
}
// Use the proper type for module with HMR
if ((module as HotModule).hot) {
(module as HotModule).hot?.accept("./components/App", () => {
const NextApp = require("./components/App").default;
root?.render(NextApp);
root?.render(
<FluentProvider theme={webLightTheme}>
<NextApp title={title} />
</FluentProvider>
);
});
}
+48
View File
@@ -0,0 +1,48 @@
/**
* Type definitions for the Office JS API
*/
/**
* Extended Error interface for Office/PowerPoint API errors
*/
export interface OfficeApiError extends Error {
code?: string;
debugInfo?: {
code?: string;
message?: string;
errorLocation?: string;
statement?: string;
innerError?: any;
};
}
/**
* Helper to safely extract error message from any error object
*/
export function getErrorMessage(error: unknown): string {
if (error instanceof Error) {
return error.message;
}
return String(error);
}
/**
* Type-safe wrapper for PowerPoint API operations on shapes
*/
export function isPictureShape(shape: PowerPoint.Shape): boolean {
return shape.type === PowerPoint.ShapeType.image;
}
/**
* Type-safe wrapper for working with PowerPoint slides
*/
export function getFirstSelectedSlide(context: PowerPoint.RequestContext): PowerPoint.Slide {
return context.presentation.getSelectedSlides().getItemAt(0);
}
/**
* Type-safe utility for selecting shapes
*/
export function selectShapesById(slide: PowerPoint.Slide, shapeIds: string[]): void {
slide.setSelectedShapes(shapeIds);
}