Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TS(2352) Declare object with dynamic properties and one property with specific type

I need to create object which will contain one property with name 'state' which will have generic type and all other properties will be functions with overriden context. I'm not sure that it is possible, because of this I decided to write to here.

I have a code:

declare interface ContextModule<State> {
  state: State
}

export declare interface SuperModule<State = any> {
  state?: State | any,
  [methodName: string]: (this: ContextModule<State>, ...args: any[]) => any
}

const lol = {
  getFoo (): any {
    return this.state.foo
  }
} as SuperModule

In this code I don't have any errors. It executes successfully, but if I will add

declare interface ContextModule<State> {
  state: State
}

export declare interface SuperModule<State = any> {
  state?: State | any,
  [methodName: string]: (this: ContextModule<State>, ...args: any[]) => any
}

const lol = {
  getFoo (): any {
    return this.state.foo
  },
+  state: {       // added this property
+    foo: 'string'
+  }
} as SuperModule

Then I will see output

Conversion of type '{ getFoo(this: ContextModule<any>): any; state: { foo: string; }; }' to type 'SuperModule<any>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
  Property 'state' is incompatible with index signature.
    Type '{ foo: string; }' is not comparable to type '(this: ContextModule<any>, ...args: any[]) => any'.
      Type '{ foo: string; }' provides no match for the signature '(this: ContextModule<any>, ...args: any[]): any'.ts(2352)

I understand that problem linked with that TypeScript trying to cast property state to [methodName: string]: (this: ContextModule<State>, ...args: any[]) => any, but why if I declared this property before declaration for dynamic properties

May be somebody saw the same problem. Hope on your help, thanks!

like image 541
Sergey Volkov Avatar asked Sep 01 '25 10:09

Sergey Volkov


1 Answers

The only way I could get this to work cleanly is using an intersection type, but it appears that the intersection of dynamic types and types with fixed properties (of differing type to the dynamic props) can only be created using Object.assign.

Here is my simplified example of how this might work:

interface MyType {
    requiredProp1: string
}
interface MyOtherType{
    [key: string]: number
}
type ISect = MyType & MyOtherType

const obj: ISect = Object.assign({ requiredProp1: "ff" }, { foo: 1 })

const rp1 = obj.requiredProp1 //string
const foo = obj.foo //number
like image 50
spender Avatar answered Sep 03 '25 03:09

spender