Refactor type handling for response handler to fix dangerous casting (#126)

This commit is contained in:
Keavon Chambers 2021-05-15 01:02:33 -07:00 committed by GitHub
parent a1b80db51f
commit 1419f3bc6b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 111 additions and 44 deletions

View file

@ -29,6 +29,8 @@ module.exports = {
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
"max-len": ["error", { code: 200, tabWidth: 4 }],
"@typescript-eslint/camelcase": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
camelcase: ["error", { allow: ["^(?:[a-z]+_)*[a-z]+$"] }],
"prettier-vue/prettier": [
"error",

View file

@ -9,7 +9,7 @@
<LayoutCol :class="'list'">
<div class="layer-row" v-for="layer in layers" :key="layer.path">
<div class="layer-visibility">
<IconButton @click="toggleLayerVisibility(layer)" :size="24" :title="layer.visible ? 'Visible' : 'Hidden'">
<IconButton @click="toggleLayerVisibility(layer.path)" :size="24" :title="layer.visible ? 'Visible' : 'Hidden'">
<EyeVisible v-if="layer.visible" />
<EyeHidden v-if="!layer.visible" />
</IconButton>

View file

@ -1,3 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
type ResponseCallback = (responseData: Response) => void;
type ResponseMap = {
[response: string]: ResponseCallback | undefined;
@ -23,42 +25,6 @@ export function registerResponseHandler(responseType: ResponseType, callback: Re
window.responseMap[responseType] = callback;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function parseResponse(origin: string, responseType: string, data: any): Response {
type OriginNames = "Document" | "Tool";
const originHandlers = {
Document: () => {
switch (responseType) {
case "DocumentChanged":
return (data.Document.DocumentChanged as DocumentChanged) as Response;
case "CollapseFolder":
return (data.Document.CollapseFolder as CollapseFolder) as Response;
case "ExpandFolder":
return (data.Document.ExpandFolder as ExpandFolder) as Response;
default:
return undefined;
}
},
Tool: () => {
switch (responseType) {
case "SetActiveTool":
return (data.Tool.SetActiveTool as SetActiveTool) as Response;
case "UpdateCanvas":
return (data.Tool.UpdateCanvas as UpdateCanvas) as Response;
default:
return undefined;
}
},
};
// TODO: Optional chaining would be nice here when we can upgrade to Webpack 5: https://github.com/webpack/webpack/issues/10227
// const response = originHandlers[origin as OriginNames]?.();
const response = originHandlers[origin as OriginNames] && originHandlers[origin as OriginNames]();
if (!response) throw new Error("ResponseType not recognized.");
return response;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function handleResponse(responseIdentifier: string, responseData: any) {
const [origin, responesType] = responseIdentifier.split("::", 2);
@ -74,32 +40,131 @@ export function handleResponse(responseIdentifier: string, responseData: any) {
}
}
enum OriginNames {
Document = "Document",
Tool = "Tool",
}
function parseResponse(origin: string, responseType: string, data: any): Response {
const response = (() => {
switch (origin) {
case OriginNames.Document:
switch (responseType) {
case "DocumentChanged":
return newDocumentChanged(data.Document.DocumentChanged);
case "CollapseFolder":
return newCollapseFolder(data.Document.CollapseFolder);
case "ExpandFolder":
return newExpandFolder(data.Document.ExpandFolder);
default:
return undefined;
}
case OriginNames.Tool:
switch (responseType) {
case "SetActiveTool":
return newSetActiveTool(data.Tool.SetActiveTool);
case "UpdateCanvas":
return newUpdateCanvas(data.Tool.UpdateCanvas);
default:
return undefined;
}
default:
return undefined;
}
})();
if (!response) throw new Error(`Unrecognized origin/responseType pair: ${origin}, ${responseType}`);
return response;
}
export type Response = SetActiveTool | UpdateCanvas | DocumentChanged | CollapseFolder | ExpandFolder;
export interface SetActiveTool {
tool_name: string;
}
function newSetActiveTool(input: any): SetActiveTool {
return {
tool_name: input.tool_name,
};
}
export interface UpdateCanvas {
document: string;
}
export type DocumentChanged = {};
export interface CollapseFolder {
path: Array<number>;
function newUpdateCanvas(input: any): UpdateCanvas {
return {
document: input.document,
};
}
export type DocumentChanged = {};
function newDocumentChanged(_: any): DocumentChanged {
return {};
}
export interface CollapseFolder {
path: BigUint64Array;
}
function newCollapseFolder(input: any): CollapseFolder {
return {
path: new BigUint64Array(input.path.map((n: number) => BigInt(n))),
};
}
export interface ExpandFolder {
path: Array<number>;
path: BigUint64Array;
children: Array<LayerPanelEntry>;
}
function newExpandFolder(input: any): ExpandFolder {
return {
path: new BigUint64Array(input.path.map((n: number) => BigInt(n))),
children: input.children.map((child: any) => newLayerPanelEntry(child)),
};
}
export interface LayerPanelEntry {
name: string;
visible: boolean;
layer_type: LayerType;
collapsed: boolean;
path: Array<number>;
path: BigUint64Array;
}
function newLayerPanelEntry(input: any): LayerPanelEntry {
return {
name: input.name,
visible: input.visible,
layer_type: newLayerType(input.layer_type),
collapsed: input.collapsed,
path: new BigUint64Array(input.path.map((n: number) => BigInt(n))),
};
}
export enum LayerType {
Folder,
Shape,
Folder = "Folder",
Shape = "Shape",
Circle = "Circle",
Rect = "Rect",
Line = "Line",
PolyLine = "PolyLine",
Ellipse = "Ellipse",
}
function newLayerType(input: any): LayerType {
switch (input) {
case "Folder":
return LayerType.Folder;
case "Shape":
return LayerType.Shape;
case "Circle":
return LayerType.Circle;
case "Rect":
return LayerType.Rect;
case "Line":
return LayerType.Line;
case "PolyLine":
return LayerType.PolyLine;
case "Ellipse":
return LayerType.Ellipse;
default:
throw Error(`Received invalid input as an enum variant for LayerType: ${input}`);
}
}