From a1626c71d88aef136bc9cefd0279621c73889c6a Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 15 Sep 2026 15:53:34 +0200 Subject: [PATCH 1/2] chore(utils): resolve no-explicit-any lint suppressions --- eslint-suppressions.json | 60 ---------------- packages/utils/CHANGELOG.md | 3 + packages/utils/src/assert.ts | 2 +- packages/utils/src/caip-types.test.ts | 14 ++-- packages/utils/src/collections.test.ts | 6 +- packages/utils/src/collections.ts | 4 +- packages/utils/src/errors.test.ts | 6 +- packages/utils/src/fs.test.ts | 52 ++++---------- packages/utils/src/json.ts | 1 + packages/utils/src/json.tst.ts | 1 + packages/utils/src/logging.test.ts | 4 +- packages/utils/src/misc.ts | 2 +- packages/utils/src/misc.tst.ts | 4 +- packages/utils/src/unitsConversion.test.ts | 82 ++++++++++++++-------- 14 files changed, 96 insertions(+), 145 deletions(-) diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 49a28c0fee..92ddb4d44e 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -2008,41 +2008,11 @@ "count": 1 } }, - "packages/utils/src/assert.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 1 - } - }, - "packages/utils/src/caip-types.test.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 4 - } - }, - "packages/utils/src/collections.test.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 2 - } - }, - "packages/utils/src/collections.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 2 - } - }, - "packages/utils/src/errors.test.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 3 - } - }, "packages/utils/src/errors.ts": { "no-restricted-syntax": { "count": 3 } }, - "packages/utils/src/fs.test.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 20 - } - }, "packages/utils/src/fs.ts": { "jsdoc/require-returns": { "count": 1 @@ -2058,31 +2028,6 @@ "count": 3 } }, - "packages/utils/src/json.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 1 - } - }, - "packages/utils/src/json.tst.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 1 - } - }, - "packages/utils/src/logging.test.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 2 - } - }, - "packages/utils/src/misc.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 1 - } - }, - "packages/utils/src/misc.tst.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 1 - } - }, "packages/utils/src/mnemonic.ts": { "jsdoc/tag-lines": { "count": 1 @@ -2092,10 +2037,5 @@ "jsdoc/tag-lines": { "count": 1 } - }, - "packages/utils/src/unitsConversion.test.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 29 - } } } diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index 8b92faad8d..67787aa2b4 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Narrowed three public type signatures from `any` to `unknown` ([#TBD](https://github.com/MetaMask/core/pull/TBD)) + - `assert` now accepts `value: unknown`, `FrozenMap.forEach` and `FrozenSet.forEach` now accept `thisArg?: unknown`, and `getKnownPropertyNames` now accepts `Partial>`. + - This is not a breaking change. `unknown` accepts every value that `any` did, so no call site needs to change. - This package was migrated from `MetaMask/utils` to the `MetaMask/core` monorepo ([#10175](https://github.com/MetaMask/core/pull/10175)) - See [MetaMask/utils](https://github.com/MetaMask/utils/blob/main/CHANGELOG.md) for the original changelog, which covers every release up to and including `12.0.0`. diff --git a/packages/utils/src/assert.ts b/packages/utils/src/assert.ts index a000aa0803..d87c777f7c 100644 --- a/packages/utils/src/assert.ts +++ b/packages/utils/src/assert.ts @@ -81,7 +81,7 @@ export class AssertionError extends Error { * the `message` argument, this argument is ignored. */ export function assert( - value: any, + value: unknown, message: string | Error = 'Assertion failed.', ErrorWrapper: AssertionErrorConstructor = AssertionError, ): asserts value { diff --git a/packages/utils/src/caip-types.test.ts b/packages/utils/src/caip-types.test.ts index fc0963c48a..3c974ef84d 100644 --- a/packages/utils/src/caip-types.test.ts +++ b/packages/utils/src/caip-types.test.ts @@ -35,6 +35,12 @@ import { toCaipAssetType, toCaipChainId, } from './caip-types.js'; +import type { + CaipAccountId, + CaipAssetId, + CaipAssetType, + CaipChainId, +} from './caip-types.js'; describe('isCaipChainId', () => { it.each(CAIP_CHAIN_ID_FIXTURES)( @@ -326,7 +332,7 @@ describe('parseCaipChainId', () => { 'foo:', 'foo:foobarbazquzfoobarbazquzfoobarbazquzfoobarbazquzfoobarbazquzfoobarbazquz', ])('throws for invalid input %s', (input) => { - expect(() => parseCaipChainId(input as any)).toThrow( + expect(() => parseCaipChainId(input as CaipChainId)).toThrow( 'Invalid CAIP chain ID.', ); }); @@ -406,7 +412,7 @@ describe('parseCaipAccountId', () => { 'eip155:1', 'eip155:1:', ])('throws for invalid input %s', (input) => { - expect(() => parseCaipAccountId(input as any)).toThrow( + expect(() => parseCaipAccountId(input as CaipAccountId)).toThrow( 'Invalid CAIP account ID.', ); }); @@ -483,7 +489,7 @@ describe('parseCaipAssetType', () => { 'eip155:1', 'eip155:1:', ])('throws for invalid input %s', (input) => { - expect(() => parseCaipAssetType(input as any)).toThrow( + expect(() => parseCaipAssetType(input as CaipAssetType)).toThrow( 'Invalid CAIP asset type.', ); }); @@ -536,7 +542,7 @@ describe('parseCaipAssetId', () => { 'eip155:1', 'eip155:1:', ])('throws for invalid input %s', (input) => { - expect(() => parseCaipAssetId(input as any)).toThrow( + expect(() => parseCaipAssetId(input as CaipAssetId)).toThrow( 'Invalid CAIP asset ID.', ); }); diff --git a/packages/utils/src/collections.test.ts b/packages/utils/src/collections.test.ts index 95cd82c927..467c47df2f 100644 --- a/packages/utils/src/collections.test.ts +++ b/packages/utils/src/collections.test.ts @@ -21,7 +21,8 @@ describe('FrozenMap', () => { }); it('is frozen and cannot be mutated', () => { - const frozenMap: any = new FrozenMap(); + // Cast so the absent mutation methods can be probed for at runtime. + const frozenMap = new FrozenMap() as unknown as Record; expect(frozenMap.set).toBeUndefined(); expect(frozenMap.clear).toBeUndefined(); @@ -247,7 +248,8 @@ describe('FrozenSet', () => { }); it('is frozen and cannot be mutated', () => { - const frozenSet: any = new FrozenSet(); + // Cast so the absent mutation methods can be probed for at runtime. + const frozenSet = new FrozenSet() as unknown as Record; expect(frozenSet.set).toBeUndefined(); expect(frozenSet.clear).toBeUndefined(); diff --git a/packages/utils/src/collections.ts b/packages/utils/src/collections.ts index 27bf11ea17..4dcc87570e 100644 --- a/packages/utils/src/collections.ts +++ b/packages/utils/src/collections.ts @@ -33,7 +33,7 @@ class FrozenMap implements ReadonlyMap { public forEach( callbackfn: (value: Value, key: Key, map: this) => void, - thisArg?: any, + thisArg?: unknown, ): void { // We have to wrap the specified callback in order to prevent it from // receiving a reference to the inner map. @@ -98,7 +98,7 @@ class FrozenSet implements ReadonlySet { public forEach( callbackfn: (value: Value, value2: Value, set: this) => void, - thisArg?: any, + thisArg?: unknown, ): void { // We have to wrap the specified callback in order to prevent it from // receiving a reference to the inner set. diff --git a/packages/utils/src/errors.test.ts b/packages/utils/src/errors.test.ts index 1e1a7cec86..900d89af57 100644 --- a/packages/utils/src/errors.test.ts +++ b/packages/utils/src/errors.test.ts @@ -129,7 +129,7 @@ describe('wrapError', () => { let originalError; try { await fs.promises.readFile('/tmp/nonexistent', 'utf8'); - } catch (error: any) { + } catch (error) { originalError = error; } @@ -142,7 +142,7 @@ describe('wrapError', () => { let originalError; try { await fs.promises.readFile('/tmp/nonexistent', 'utf8'); - } catch (error: any) { + } catch (error) { originalError = error; } @@ -155,7 +155,7 @@ describe('wrapError', () => { let originalError; try { await fs.promises.readFile('/tmp/nonexistent', 'utf8'); - } catch (error: any) { + } catch (error) { originalError = error; } diff --git a/packages/utils/src/fs.test.ts b/packages/utils/src/fs.test.ts index 8fcbceb40f..c63af78800 100644 --- a/packages/utils/src/fs.test.ts +++ b/packages/utils/src/fs.test.ts @@ -259,15 +259,8 @@ describe('fs', () => { await withinSandbox(async (sandbox) => { const filePath = path.join(sandbox.directoryPath, 'test.json'); - const stringifier = { - stringify( - json: any, - replacer?: - | ((this: any, key: string, value: any) => any) - | (number | string)[] - | null, - space?: string, - ): string { + const stringifier: { stringify: typeof JSON.stringify } = { + stringify(json, replacer, space) { return ( `${util.inspect(json)}\n` + `replacer: ${util.inspect(replacer)}, space: ${util.inspect( @@ -290,15 +283,8 @@ describe('fs', () => { await withinSandbox(async (sandbox) => { const filePath = path.join(sandbox.directoryPath, 'test.json'); - const stringifier = { - stringify( - json: any, - replacer?: - | ((this: any, key: string, value: any) => any) - | (number | string)[] - | null, - space?: string, - ): string { + const stringifier: { stringify: typeof JSON.stringify } = { + stringify(json, replacer, space) { return ( `${util.inspect(json)}\n` + `replacer: ${util.inspect(replacer)}, space: ${util.inspect( @@ -325,15 +311,8 @@ describe('fs', () => { await withinSandbox(async (sandbox) => { const filePath = path.join(sandbox.directoryPath, 'test.json'); - const stringifier = { - stringify( - json: any, - replacer?: - | ((this: any, key: string, value: any) => any) - | (number | string)[] - | null, - space?: string, - ): string { + const stringifier: { stringify: typeof JSON.stringify } = { + stringify(json, replacer, space) { return ( `${util.inspect(json)}\n` + `replacer: ${util.inspect(replacer)}, space: ${util.inspect( @@ -362,15 +341,8 @@ describe('fs', () => { // Make sandbox root directory non-readable await fs.promises.chmod(sandbox.directoryPath, 0o600); const filePath = path.join(sandbox.directoryPath, 'test.json'); - const stringifier = { - stringify( - json: any, - replacer?: - | ((this: any, key: string, value: any) => any) - | (number | string)[] - | null, - space?: string, - ): string { + const stringifier: { stringify: typeof JSON.stringify } = { + stringify(json, replacer, space) { return ( `${util.inspect(json)}\n` + `replacer: ${util.inspect(replacer)}, space: ${util.inspect( @@ -435,7 +407,7 @@ describe('fs', () => { it('re-throws a wrapped version of any error that occurs, assigning it the same code and giving it a stack', async () => { const entryPath = '/some/file'; - const error: any = new Error('oops'); + const error: Error & { code?: string } = new Error('oops'); error.code = 'ESOMETHING'; error.stack = 'some stack'; when(jest.spyOn(fs.promises, 'stat')) @@ -494,7 +466,7 @@ describe('fs', () => { it('re-throws a wrapped version of any error that occurs, assigning it the same code and giving it a stack', async () => { const entryPath = '/some/file'; - const error: any = new Error('oops'); + const error: Error & { code?: string } = new Error('oops'); error.code = 'ESOMETHING'; error.stack = 'some stack'; when(jest.spyOn(fs.promises, 'stat')) @@ -604,7 +576,7 @@ describe('fs', () => { it('re-throws a wrapped version of any error that occurs, assigning it the same code and giving it a stack', async () => { const filePath = '/some/file'; - const error: any = new Error('oops'); + const error: Error & { code?: string } = new Error('oops'); error.code = 'ESOMETHING'; error.stack = 'some stack'; when(jest.spyOn(fs.promises, 'rm')) @@ -655,7 +627,7 @@ describe('fs', () => { it('re-throws a wrapped version of any error that occurs, assigning it the same code and giving it a stack', async () => { const directoryPath = '/some/directory'; - const error: any = new Error('oops'); + const error: Error & { code?: string } = new Error('oops'); error.code = 'ESOMETHING'; error.stack = 'some stack'; when(jest.spyOn(fs.promises, 'rm')) diff --git a/packages/utils/src/json.ts b/packages/utils/src/json.ts index b2795c477d..02e3ff072a 100644 --- a/packages/utils/src/json.ts +++ b/packages/utils/src/json.ts @@ -344,6 +344,7 @@ export const JsonRpcRequestStruct = object({ }); export type InferWithParams< + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- `Struct` is superstruct's idiom for "any struct". `Struct` does not work: `Struct` holds `refiner` as a property, which puts the value type in a contravariant position, so no concrete struct satisfies the constraint. Type extends Struct, Params extends JsonRpcParams, > = Infer & { diff --git a/packages/utils/src/json.tst.ts b/packages/utils/src/json.tst.ts index 9b48b67479..516396ba35 100644 --- a/packages/utils/src/json.tst.ts +++ b/packages/utils/src/json.tst.ts @@ -133,6 +133,7 @@ describe('Json', () => { test('has known edge cases', () => { // The Json type doesn't protect against the `any` type. Passing `any` // explicitly is the point of the test, so it must not be removed. + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- The `any` under test cannot be replaced without inverting what this assertion checks. expect().type.toBeAssignableTo(); // The Json type gets confused by interfaces. This interface is valid Json, diff --git a/packages/utils/src/logging.test.ts b/packages/utils/src/logging.test.ts index 1f0c75a0d0..ecf4741edb 100644 --- a/packages/utils/src/logging.test.ts +++ b/packages/utils/src/logging.test.ts @@ -19,7 +19,7 @@ describe('logging', () => { log.enabled = true; // Typecast: The Debugger type is wrong and does not include a `useColors` // property. - (log as any).useColors = false; + (log as unknown as { useColors: boolean }).useColors = false; log('Some message'); @@ -38,7 +38,7 @@ describe('logging', () => { log.enabled = true; // Typecast: The Debugger type is wrong and does not include a `useColors` // property. - (log as any).useColors = false; + (log as unknown as { useColors: boolean }).useColors = false; log('Some message'); diff --git a/packages/utils/src/misc.ts b/packages/utils/src/misc.ts index c3a28003bc..a7c031a200 100644 --- a/packages/utils/src/misc.ts +++ b/packages/utils/src/misc.ts @@ -120,7 +120,7 @@ export const hasProperty = < * derived from the object itself. */ export function getKnownPropertyNames( - object: Partial>, + object: Partial>, ): Key[] { return Object.getOwnPropertyNames(object) as Key[]; } diff --git a/packages/utils/src/misc.tst.ts b/packages/utils/src/misc.tst.ts index db54f512d4..69eaaa288d 100644 --- a/packages/utils/src/misc.tst.ts +++ b/packages/utils/src/misc.tst.ts @@ -103,7 +103,9 @@ describe('hasProperty', () => { exampleErrorWithCode.code = 999; // Establish that trying to check for a custom property on an error results in failure - expect(exampleErrorWithCode).type.not.toBeAssignableTo<{ code: any }>(); + expect(exampleErrorWithCode).type.not.toBeAssignableTo<{ + code: unknown; + }>(); if (hasProperty(exampleErrorWithCode, 'code')) { expect(exampleErrorWithCode.code).type.toBe(); diff --git a/packages/utils/src/unitsConversion.test.ts b/packages/utils/src/unitsConversion.test.ts index 51d5c43a62..5e97f8bf47 100644 --- a/packages/utils/src/unitsConversion.test.ts +++ b/packages/utils/src/unitsConversion.test.ts @@ -12,6 +12,12 @@ import { // Import the internal function for testing (note: this would normally be exported for testing) // For now we'll test it indirectly through the public functions +// `EthereumUnit` and the numeric argument union are not exported, but the tests +// below deliberately pass values outside them to exercise the runtime +// validation, so they are reconstructed here to cast against. +type EthereumUnit = keyof typeof unitMap; +type Numeric = string | number | bigint; + const totalTypes = Object.keys(unitMap).length; /** @@ -109,17 +115,21 @@ describe('getValueOfUnit', () => { }); it('should handle case insensitive input', () => { - expect(getValueOfUnit('ETHER' as any)).toBe(BigInt('1000000000000000000')); - expect(getValueOfUnit('Ether' as any)).toBe(BigInt('1000000000000000000')); - expect(getValueOfUnit('GWEI' as any)).toBe(BigInt('1000000000')); + expect(getValueOfUnit('ETHER' as EthereumUnit)).toBe( + BigInt('1000000000000000000'), + ); + expect(getValueOfUnit('Ether' as EthereumUnit)).toBe( + BigInt('1000000000000000000'), + ); + expect(getValueOfUnit('GWEI' as EthereumUnit)).toBe(BigInt('1000000000')); expect(getValueOfUnit('Gwei')).toBe(BigInt('1000000000')); }); it('should throw error for invalid units', () => { - expect(() => getValueOfUnit('invalidunit' as any)).toThrow( + expect(() => getValueOfUnit('invalidunit' as EthereumUnit)).toThrow( "The unit provided invalidunit doesn't exist", ); - expect(() => getValueOfUnit('' as any)).toThrow( + expect(() => getValueOfUnit('' as EthereumUnit)).toThrow( "The unit provided doesn't exist", ); }); @@ -130,8 +140,8 @@ describe('getValueOfUnit', () => { // Test that all units from unitMap are supported Object.keys(unitMap).forEach((unit) => { - expect(() => getValueOfUnit(unit as any)).not.toThrow(); - expect(getValueOfUnit(unit as any)).toBe( + expect(() => getValueOfUnit(unit as EthereumUnit)).not.toThrow(); + expect(getValueOfUnit(unit as EthereumUnit)).toBe( BigInt(unitMap[unit as keyof typeof unitMap]), ); }); @@ -139,12 +149,18 @@ describe('getValueOfUnit', () => { it('should handle optimized unit lookups correctly', () => { // Test that invalid units throw errors with optimized lookups - expect(() => toWei(BigInt(1), 'invalidunit' as any)).toThrow(Error); - expect(() => fromWei(BigInt(1000), 'invalidunit' as any)).toThrow(Error); + expect(() => toWei(BigInt(1), 'invalidunit' as EthereumUnit)).toThrow( + Error, + ); + expect(() => fromWei(BigInt(1000), 'invalidunit' as EthereumUnit)).toThrow( + Error, + ); // Test case insensitive lookups work - expect(() => toWei(1, 'ETHER' as any)).not.toThrow(); - expect(() => fromWei(1000000000000000000, 'GWEI' as any)).not.toThrow(); + expect(() => toWei(1, 'ETHER' as EthereumUnit)).not.toThrow(); + expect(() => + fromWei(1000000000000000000, 'GWEI' as EthereumUnit), + ).not.toThrow(); }); }); @@ -174,7 +190,7 @@ describe('toWei', () => { // Test case sensitivity with BigInt (should work with optimized lookup) expect(toWei(BigInt(1), 'Gwei')).toBe(BigInt('1000000000')); - expect(toWei(BigInt(1), 'ETHER' as any)).toBe( + expect(toWei(BigInt(1), 'ETHER' as EthereumUnit)).toBe( BigInt('1000000000000000000'), ); }); @@ -291,7 +307,7 @@ describe('toWei', () => { ); expect(() => { - toWei(1, 'wei1' as any); + toWei(1, 'wei1' as EthereumUnit); }).toThrow(Error); }); }); @@ -299,10 +315,12 @@ describe('toWei', () => { describe('numberToString', () => { it('should handle edge cases', () => { // expect(() => numberToString(null)).toThrow(Error); - expect(() => numberToString(undefined as any)).toThrow(Error); + expect(() => numberToString(undefined as unknown as Numeric)).toThrow( + Error, + ); // expect(() => numberToString(NaN)).toThrow(Error); - expect(() => numberToString({} as any)).toThrow(Error); - expect(() => numberToString([] as any)).toThrow(Error); + expect(() => numberToString({} as unknown as Numeric)).toThrow(Error); + expect(() => numberToString([] as unknown as Numeric)).toThrow(Error); expect(() => numberToString('-1sdffsdsdf')).toThrow(Error); expect(() => numberToString('-0..-...9')).toThrow(Error); expect(() => numberToString('fds')).toThrow(Error); @@ -419,7 +437,9 @@ describe('fromWei', () => { // Test case sensitivity with BigInt expect(fromWei(BigInt('1000000000'), 'Gwei')).toBe('1'); - expect(fromWei(BigInt('1000000000000000000'), 'ETHER' as any)).toBe('1'); + expect( + fromWei(BigInt('1000000000000000000'), 'ETHER' as EthereumUnit), + ).toBe('1'); // Test large BigInt values expect(fromWei(BigInt('999000000000000000000'), 'ether')).toBe('999'); @@ -557,16 +577,16 @@ describe('numericToBigInt', () => { }); it('should throw error for invalid input types', () => { - expect(() => numericToBigInt(null as any)).toThrow( + expect(() => numericToBigInt(null as unknown as Numeric)).toThrow( 'Cannot convert object to BigInt', ); - expect(() => numericToBigInt(undefined as any)).toThrow( + expect(() => numericToBigInt(undefined as unknown as Numeric)).toThrow( 'Cannot convert undefined to BigInt', ); - expect(() => numericToBigInt({} as any)).toThrow( + expect(() => numericToBigInt({} as unknown as Numeric)).toThrow( 'Cannot convert object to BigInt', ); - expect(() => numericToBigInt(true as any)).toThrow( + expect(() => numericToBigInt(true as unknown as Numeric)).toThrow( 'Cannot convert boolean to BigInt', ); }); @@ -618,7 +638,7 @@ describe('units', () => { ]; testCases.forEach(({ input, unit, expected }) => { - expect(toWei(input, unit as any)).toBe(expected); + expect(toWei(input, unit as EthereumUnit)).toBe(expected); }); }); @@ -675,8 +695,8 @@ describe('units', () => { 'ETHER', ]; testUnits.forEach((unit) => { - expect(() => toWei(BigInt(1), unit as any)).not.toThrow(); - expect(() => fromWei(BigInt(1000), unit as any)).not.toThrow(); + expect(() => toWei(BigInt(1), unit as EthereumUnit)).not.toThrow(); + expect(() => fromWei(BigInt(1000), unit as EthereumUnit)).not.toThrow(); }); // Test that optimized paths produce identical results to original paths @@ -684,7 +704,9 @@ describe('units', () => { const testUnitsForComparison = ['wei', 'gwei', 'ether']; testUnitsForComparison.forEach((unit) => { - const results = testValues.map((value) => toWei(value, unit as any)); + const results = testValues.map((value) => + toWei(value, unit as EthereumUnit), + ); // All results should be identical expect(results[0]).toBe(results[1]); expect(results[1]).toBe(results[2]); @@ -698,10 +720,12 @@ describe('units', () => { expect(typeof toWei(123, 'wei')).toBe('bigint'); // Test that invalid types would throw (tested through public API) - expect(() => toWei({} as any, 'wei')).toThrow(Error); - expect(() => toWei([] as any, 'wei')).toThrow(Error); - expect(() => toWei(null as any, 'wei')).toThrow(Error); - expect(() => toWei(undefined as any, 'wei')).toThrow(Error); + expect(() => toWei({} as unknown as Numeric, 'wei')).toThrow(Error); + expect(() => toWei([] as unknown as Numeric, 'wei')).toThrow(Error); + expect(() => toWei(null as unknown as Numeric, 'wei')).toThrow(Error); + expect(() => toWei(undefined as unknown as Numeric, 'wei')).toThrow( + Error, + ); }); }); }); From 804191f4a9720ca49406a2c923f77af685b0f8ee Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 15 Sep 2026 15:59:38 +0200 Subject: [PATCH 2/2] chore(utils): link changelog entry to pull request --- packages/utils/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index 67787aa2b4..feac41c6b3 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Narrowed three public type signatures from `any` to `unknown` ([#TBD](https://github.com/MetaMask/core/pull/TBD)) +- Narrowed three public type signatures from `any` to `unknown` ([#10249](https://github.com/MetaMask/core/pull/10249)) - `assert` now accepts `value: unknown`, `FrozenMap.forEach` and `FrozenSet.forEach` now accept `thisArg?: unknown`, and `getKnownPropertyNames` now accepts `Partial>`. - This is not a breaking change. `unknown` accepts every value that `any` did, so no call site needs to change. - This package was migrated from `MetaMask/utils` to the `MetaMask/core` monorepo ([#10175](https://github.com/MetaMask/core/pull/10175))