diff --git a/src/core/Ros.ts b/src/core/Ros.ts index e64c7f70c..4a43437fe 100644 --- a/src/core/Ros.ts +++ b/src/core/Ros.ts @@ -646,6 +646,11 @@ export default class Ros extends EventEmitter< const arrayLen = theType.fieldarraylen[i]; const fieldName = theType.fieldnames[i]; const fieldType = theType.fieldtypes[i]; + if (fieldName === undefined || fieldType === undefined) { + throw new Error( + "Received mismatched type definition vector lengths!", + ); + } if (!fieldType.includes("/")) { // check the fieldType includes '/' or not if (arrayLen === -1) { @@ -677,7 +682,11 @@ export default class Ros extends EventEmitter< return typeDefDict; }; - return decodeTypeDefsRec(defs[0], defs); + if (defs[0]) { + return decodeTypeDefsRec(defs[0], defs); + } else { + return {}; + } } /** diff --git a/src/urdf/UrdfBox.ts b/src/urdf/UrdfBox.ts index 4561d7a1b..184a8cb33 100644 --- a/src/urdf/UrdfBox.ts +++ b/src/urdf/UrdfBox.ts @@ -22,7 +22,7 @@ export default class UrdfBox { const size: Optional = xml .getAttribute(UrdfAttrs.Size) ?.split(" "); - if (size?.length !== 3) { + if (!(size?.[0] && size[1] && size[2])) { return; } diff --git a/src/urdf/UrdfColor.ts b/src/urdf/UrdfColor.ts index 0cc42b569..cc1127aa5 100644 --- a/src/urdf/UrdfColor.ts +++ b/src/urdf/UrdfColor.ts @@ -33,7 +33,7 @@ export default class UrdfColor { const rgba: Optional = xml .getAttribute(UrdfAttrs.Rgba) ?.split(" "); - if (rgba?.length !== 4) { + if (!(rgba?.[0] && rgba[1] && rgba[2] && rgba[3])) { return; } diff --git a/src/urdf/UrdfJoint.ts b/src/urdf/UrdfJoint.ts index 43a6ac3ae..7e0026ff7 100644 --- a/src/urdf/UrdfJoint.ts +++ b/src/urdf/UrdfJoint.ts @@ -31,17 +31,17 @@ export default class UrdfJoint { this.type = xml.getAttribute(UrdfAttrs.Type); const parents = xml.getElementsByTagName(UrdfAttrs.Parent); - if (parents.length > 0) { + if (parents[0]) { this.parent = parents[0].getAttribute(UrdfAttrs.Link); } const children = xml.getElementsByTagName(UrdfAttrs.Child); - if (children.length > 0) { + if (children[0]) { this.child = children[0].getAttribute(UrdfAttrs.Link); } const limits = xml.getElementsByTagName(UrdfAttrs.Limit); - if (limits.length > 0) { + if (limits[0]) { this.minval = parseFloat( limits[0].getAttribute(UrdfAttrs.Lower) ?? "NaN", ); @@ -52,12 +52,12 @@ export default class UrdfJoint { // Origin const origins = xml.getElementsByTagName(UrdfAttrs.Origin); - if (origins.length > 0) { + if (origins[0]) { this.origin = parseUrdfOrigin(origins[0]); } const axis = xml.getElementsByTagName(UrdfAttrs.Axis); - if (axis.length > 0) { + if (axis[0]) { const xyzValue = axis[0].getAttribute(UrdfAttrs.Xyz)?.split(" "); if (!xyzValue || xyzValue.length !== 3) { throw new Error( diff --git a/src/urdf/UrdfMaterial.ts b/src/urdf/UrdfMaterial.ts index 210e81c5b..385f360aa 100644 --- a/src/urdf/UrdfMaterial.ts +++ b/src/urdf/UrdfMaterial.ts @@ -21,13 +21,13 @@ export default class UrdfMaterial { // Texture const textures = xml.getElementsByTagName(UrdfAttrs.Texture); - if (textures.length > 0) { + if (textures[0]) { this.textureFilename = textures[0].getAttribute(UrdfAttrs.Filename); } // Color const colors = xml.getElementsByTagName(UrdfAttrs.Color); - if (colors.length > 0) { + if (colors[0]) { // Parse the RBGA string this.color = new UrdfColor({ xml: colors[0], diff --git a/src/urdf/UrdfMesh.ts b/src/urdf/UrdfMesh.ts index f15d75a68..c876d1bcc 100644 --- a/src/urdf/UrdfMesh.ts +++ b/src/urdf/UrdfMesh.ts @@ -24,7 +24,7 @@ export default class UrdfMesh { const scale: Optional = xml .getAttribute(UrdfAttrs.Scale) ?.split(" "); - if (scale?.length !== 3) { + if (!(scale?.[0] && scale[1] && scale[2])) { return; } diff --git a/src/urdf/UrdfModel.ts b/src/urdf/UrdfModel.ts index 084ca82a1..0a8e5cd4e 100644 --- a/src/urdf/UrdfModel.ts +++ b/src/urdf/UrdfModel.ts @@ -72,8 +72,9 @@ export default class UrdfModel { break; } - if (this.materials[material.name].isLink()) { - this.materials[material.name].assign(material); + const existingMaterial = this.materials[material.name]; + if (existingMaterial?.isLink()) { + existingMaterial.assign(material); } else { console.warn(`Material ${material.name} is not unique.`); } @@ -95,8 +96,9 @@ export default class UrdfModel { continue; } - if (Object.hasOwn(this.materials, mat.name)) { - item.material = this.materials[mat.name]; + const material = this.materials[mat.name]; + if (material) { + item.material = material; } else { this.materials[mat.name] = mat; } diff --git a/src/urdf/UrdfUtils.ts b/src/urdf/UrdfUtils.ts index 8a09d6424..63940947c 100644 --- a/src/urdf/UrdfUtils.ts +++ b/src/urdf/UrdfUtils.ts @@ -11,7 +11,7 @@ export function parseUrdfOrigin(originElement: Element): Pose { .getAttribute(UrdfAttrs.Xyz) ?.split(" "); let position: Vector3 = new Vector3(); - if (xyz?.length === 3) { + if (xyz?.[0] && xyz[1] && xyz[2]) { position = new Vector3({ x: parseFloat(xyz[0]), y: parseFloat(xyz[1]), @@ -22,7 +22,7 @@ export function parseUrdfOrigin(originElement: Element): Pose { // Check the RPY const rpy = originElement.getAttribute(UrdfAttrs.Rpy)?.split(" "); let orientation = new Quaternion(); - if (rpy?.length === 3) { + if (rpy?.[0] && rpy[1] && rpy[2]) { // Convert from RPY const roll = parseFloat(rpy[0]); const pitch = parseFloat(rpy[1]); diff --git a/src/urdf/UrdfVisual.ts b/src/urdf/UrdfVisual.ts index a166f1de0..17dc238d7 100644 --- a/src/urdf/UrdfVisual.ts +++ b/src/urdf/UrdfVisual.ts @@ -63,19 +63,19 @@ export default class UrdfVisual { // Origin const origins = xml.getElementsByTagName(UrdfAttrs.Origin); - if (origins.length > 0) { + if (origins[0]) { this.origin = parseUrdfOrigin(origins[0]); } // Geometry const geoms = xml.getElementsByTagName(UrdfAttrs.Geometry); - if (geoms.length > 0) { + if (geoms[0]) { this.geometry = parseUrdfGeometry(geoms[0]); } // Material const materials = xml.getElementsByTagName(UrdfAttrs.Material); - if (materials.length > 0) { + if (materials[0]) { this.material = new UrdfMaterial({ xml: materials[0], }); diff --git a/src/util/cborTypedArrayTags.ts b/src/util/cborTypedArrayTags.ts index ed5a18b36..ef7166e03 100644 --- a/src/util/cborTypedArrayTags.ts +++ b/src/util/cborTypedArrayTags.ts @@ -29,6 +29,9 @@ function decodeUint64LE(bytes: Uint8Array) { const si = i * 2; const lo = uint32View[si]; const hi = uint32View[si + 1]; + if (lo === undefined || hi === undefined) { + throw new Error("Invalid byte array"); + } arr[i] = lo + UPPER32 * hi; } @@ -55,6 +58,9 @@ function decodeInt64LE(bytes: Uint8Array) { const si = i * 2; const lo = uint32View[si]; const hi = int32View[si + 1]; + if (lo === undefined || hi === undefined) { + throw new Error("Invalid byte array"); + } arr[i] = lo + UPPER32 * hi; } @@ -120,12 +126,12 @@ export default function cborTypedArrayTagger( data: Uint8Array, tag: number, ) { - if (tag in nativeArrayTypes) { - const arrayType = nativeArrayTypes[tag]; + const arrayType = nativeArrayTypes[tag]; + if (arrayType) { return decodeNativeArray(data, arrayType); } if (tag in conversionArrayTypes) { - return conversionArrayTypes[tag](data); + return conversionArrayTypes[tag]?.(data); } return data; } diff --git a/test/examples/params.example.ts b/test/examples/params.example.ts index e3c656551..0482b6b8b 100644 --- a/test/examples/params.example.ts +++ b/test/examples/params.example.ts @@ -57,7 +57,7 @@ describe("Param setting", function () { ros.getParams(callback); await vi.waitFor(() => { expect(callback).toHaveBeenCalledOnce(); - expect(callback.mock.calls[0][0]).to.include(PARAM_NAME); + expect(callback.mock.calls[0]?.[0]).to.include(PARAM_NAME); }); }, ); @@ -74,7 +74,7 @@ describe("Param setting", function () { const getParamsCallback = vi.fn(); ros.getParams(getParamsCallback); await vi.waitFor(() => - expect(getParamsCallback.mock.calls[0][0]).to.not.include(PARAM_NAME), + expect(getParamsCallback.mock.calls[0]?.[0]).to.not.include(PARAM_NAME), ); }, ); diff --git a/test/urdf.test.ts b/test/urdf.test.ts index 001ca8441..6e280ad81 100644 --- a/test/urdf.test.ts +++ b/test/urdf.test.ts @@ -88,70 +88,68 @@ describe("URDF", function () { }); // Check all the visual elements - expect(urdfModel.links["link1"].visuals.length).to.equal(1); + expect(urdfModel.links["link1"]?.visuals.length).to.equal(1); if ( - !(urdfModel.links["link1"].visuals[0]?.geometry instanceof UrdfSphere) + !(urdfModel.links["link1"]?.visuals[0]?.geometry instanceof UrdfSphere) ) { throw new Error("Expected geometry to be an instance of UrdfSphere"); } - expect(urdfModel.links["link1"].visuals[0]?.geometry?.radius).to.equal( - 1.0, - ); - if (!(urdfModel.links["link2"].visuals[0]?.geometry instanceof UrdfBox)) { + expect(urdfModel.links["link1"].visuals[0].geometry.radius).to.equal(1.0); + if ( + !(urdfModel.links["link2"]?.visuals[0]?.geometry instanceof UrdfBox) + ) { throw new Error("Expected geometry to be an instance of UrdfBox"); } expect( - urdfModel.links["link2"].visuals[0]?.geometry?.dimension?.x, + urdfModel.links["link2"].visuals[0].geometry.dimension?.x, ).to.equal(0.5); expect( - urdfModel.links["link2"].visuals[0]?.geometry?.dimension?.y, + urdfModel.links["link2"].visuals[0].geometry.dimension?.y, ).to.equal(0.5); expect( - urdfModel.links["link2"].visuals[0]?.geometry?.dimension?.z, + urdfModel.links["link2"].visuals[0].geometry.dimension?.z, ).to.equal(0.5); if ( - !(urdfModel.links["link3"].visuals[0]?.geometry instanceof UrdfCylinder) + !( + urdfModel.links["link3"]?.visuals[0]?.geometry instanceof UrdfCylinder + ) ) { throw new Error("Expected geometry to be an instance of UrdfCylinder"); } - expect(urdfModel.links["link3"].visuals[0]?.geometry?.length).to.equal( - 2.0, - ); - expect(urdfModel.links["link3"].visuals[0]?.geometry?.radius).to.equal( - 0.2, - ); + expect(urdfModel.links["link3"].visuals[0].geometry.length).to.equal(2.0); + expect(urdfModel.links["link3"].visuals[0].geometry.radius).to.equal(0.2); - expect(urdfModel.links["link4"].visuals.length).to.equal(1); - expect(urdfModel.links["link4"].visuals[0]?.material?.name).to.equal( + expect(urdfModel.links["link4"]?.visuals.length).to.equal(1); + expect(urdfModel.links["link4"]?.visuals[0]?.material?.name).to.equal( "red", ); - expect(urdfModel.links["link4"].visuals[0]?.material?.color?.r).to.equal( + expect(urdfModel.links["link4"]?.visuals[0]?.material?.color?.r).to.equal( 1.0, ); - expect(urdfModel.links["link4"].visuals[0]?.material?.color?.g).to.equal( + expect(urdfModel.links["link4"]?.visuals[0]?.material?.color?.g).to.equal( 0, ); - expect(urdfModel.links["link4"].visuals[0]?.material?.color?.b).to.equal( + expect(urdfModel.links["link4"]?.visuals[0]?.material?.color?.b).to.equal( 0, ); - expect(urdfModel.links["link4"].visuals[0]?.material?.color?.a).to.equal( + expect(urdfModel.links["link4"]?.visuals[0]?.material?.color?.a).to.equal( 1.0, ); - expect(urdfModel.links["link5"].visuals.length).to.equal(2); - expect(urdfModel.links["link5"].visuals[0]?.material?.name).to.equal( + expect(urdfModel.links["link5"]?.visuals.length).to.equal(2); + expect(urdfModel.links["link5"]?.visuals[0]?.material?.name).to.equal( "blue", ); - expect(urdfModel.links["link5"].visuals[0]?.material?.color?.r).to.equal( + expect(urdfModel.links["link5"]?.visuals[0]?.material?.color?.r).to.equal( 0.0, ); - expect(urdfModel.links["link5"].visuals[0]?.material?.color?.g).to.equal( + expect(urdfModel.links["link5"]?.visuals[0]?.material?.color?.g).to.equal( 0.0, ); - expect(urdfModel.links["link5"].visuals[0]?.material?.color?.b).to.equal( + expect(urdfModel.links["link5"]?.visuals[0]?.material?.color?.b).to.equal( 1.0, ); - expect(urdfModel.links["link5"].visuals[0]?.material?.color?.a).to.equal( + expect(urdfModel.links["link5"]?.visuals[0]?.material?.color?.a).to.equal( 1.0, ); }); diff --git a/tsconfig.json b/tsconfig.json index 19212b3fe..06d1bd156 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -49,11 +49,11 @@ "strictFunctionTypes": true /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */, "strictPropertyInitialization": true /* Check for class properties that are declared but not set in the constructor. */, "noImplicitOverride": true /* Ensure overriding members in derived classes are marked with an override modifier. */, + "noUncheckedIndexedAccess": true /* Add `undefined` to a type when accessed using an index. */, /* Rules to be enabled */ "exactOptionalPropertyTypes": false /* Differentiate between undefined and not present when type checking */, "noImplicitAny": false /* Enable error reporting for expressions and declarations with an implied 'any' type. */, - "noUncheckedIndexedAccess": false /* Add `undefined` to a type when accessed using an index. */, "types": ["@types/node"] },