From eda5b9dfa87ab9418b77709a0a020a7ffefe9d92 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Mon, 26 Feb 2024 19:08:49 -0500 Subject: [PATCH] [base-controller] Fix `any` usage in `BaseControllerV1` (#3959) ## Explanation - For runtime property assignment, use `as unknown as` instead of `as any`. - Change the types for `BaseControllerV1` class fields `initialConfig`, `initialState` from `C`, `S` to `Partial`, `Partial`. - Initial user-supplied constructor options do not need to be complete `C`, `S` objects, since `internal{Config,State}` will be populated with `default{Config,State}`. - For empty objects, prefer no type assertions or `as never` (`never` is assignable to all types). - Fix code written based on outdated TypeScript limitation. - Generic spread expressions for object literals are supported by TypeScript: https://github.com/microsoft/TypeScript/pull/28234 ## References - Closes #3715 ## Changelog ### [`@metamask/base-controller`](https://github.com/MetaMask/core/pull/3959/files#diff-a8212838da15b445582e5622bd4cc8195e4c52bcf87210af8074555f806706a9) ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've highlighted breaking changes using the "BREAKING" category above as appropriate --- .../base-controller/src/BaseControllerV1.ts | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/packages/base-controller/src/BaseControllerV1.ts b/packages/base-controller/src/BaseControllerV1.ts index 30efb445694..c075684fa8e 100644 --- a/packages/base-controller/src/BaseControllerV1.ts +++ b/packages/base-controller/src/BaseControllerV1.ts @@ -42,12 +42,12 @@ export class BaseControllerV1 { /** * Default options used to configure this controller */ - defaultConfig: C = {} as C; + defaultConfig: C = {} as never; /** * Default state set on this controller */ - defaultState: S = {} as S; + defaultState: S = {} as never; /** * Determines if listeners are notified of state changes @@ -59,9 +59,9 @@ export class BaseControllerV1 { */ name = 'BaseController'; - private readonly initialConfig: C; + private readonly initialConfig: Partial; - private readonly initialState: S; + private readonly initialState: Partial; private internalConfig: C = this.defaultConfig; @@ -76,10 +76,9 @@ export class BaseControllerV1 { * @param config - Initial options used to configure this controller. * @param state - Initial state to set on this controller. */ - constructor(config: Partial = {} as C, state: Partial = {} as S) { - // Use assign since generics can't be spread: https://git.io/vpRhY - this.initialState = state as S; - this.initialConfig = config as C; + constructor(config: Partial = {}, state: Partial = {}) { + this.initialState = state; + this.initialConfig = config; } /** @@ -128,21 +127,19 @@ export class BaseControllerV1 { ? (config as C) : Object.assign(this.internalConfig, config); - for (const [key, value] of Object.entries(this.internalConfig)) { + for (const key of Object.keys(this.internalConfig) as (keyof C)[]) { + const value = this.internalConfig[key]; if (value !== undefined) { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (this as any)[key] = value; + (this as unknown as C)[key] = value; } } } else { for (const key of Object.keys(config) as (keyof C)[]) { /* istanbul ignore else */ - if (typeof this.internalConfig[key] !== 'undefined') { - this.internalConfig[key] = (config as C)[key]; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (this as any)[key] = config[key]; + if (this.internalConfig[key] !== undefined) { + const value = (config as C)[key]; + this.internalConfig[key] = value; + (this as unknown as C)[key] = value; } } }