0

The parent class method updates the properties of the child class, but I get these error messages. How can I solve this problem.

index.ts code:

class A {
  animate<
    K extends keyof this,
    T extends {
      [key in K]: this[key] extends number ? this[key] : never
    }
  >(props: Partial<T>) {

    const initialValue: Partial<T> = {}
    const changeValue: Partial<T> = {}

    for (let key in props) {
      initialValue[key] = this[key]
      changeValue[key] = props[key] - this[key]
    }

    // ...
  }
}

class B extends A {
  width: number
  heigth: number
  name: string
  constructor() {
    super();
    this.heigth = 20
    this.width = 20
    this.name = 'B'
  }
}

const b = new B();

b.animate({
  width: 40,
  heigth: 40
})

Error message:

ERROR in /project/app/index.ts(13,27) TS2536: Type 'Extract<keyof T, string>' cannot be used to index type 'this'.

ERROR in /project/app/index.ts(14,7) TS2322: Type 'number' is not assignable to type 'T[Extract<keyof T, string>] | undefined'.

ERROR in /project/app/index.ts(14,26) TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.

ERROR in /project/app/index.ts(14,39) TS2536: Type 'Extract<keyof T, string>' cannot be used to index type 'this'.

Gang Yang
  • 1
  • 1
  • 1

1 Answers1

0

First of all, class A knows nothing about this of B.

this of class A has only one method animate.

I believe that you should declare animate property in class B. Do you have a control over the A class ? This line [key in K]: this[key] extends number ? this[key] : never will not work in a way you expect.

Both A and B have different this context.

Consider this example:


class A {
  animate<
    K extends keyof this,
    T extends {
      [key in K]: number
    }
  >(this: Record<string, T[Extract<keyof T, string>]>, props: T) {

    const initialValue: Partial<T> = {}
    const changeValue: Record<string, number> = {}

    for (let key in props) {
      initialValue[key] = this[key]
      changeValue[key] = props[key] - this[key]
    }

    // ...
    return {initialValue, changeValue}
  }
}

class B extends A {
  width: number
  heigth: number
  name: string

  constructor() {
    super();
    this.heigth = 20
    this.width = 20
    this.name = 'B'
  }
}
const b = new B();

const result = b.animate({ // <-------------------- Error
  width: 40,
  heigth: 40
})

The 'this' context of type 'B' is not assignable to method's 'this' of type 'Record<string, number>'. Index signature is missing in type 'B'

While there are no errors inside class implementation, you can't call b with context of A.

If you don't want to change inheritance model you can use this code as a workaround:

interface Indexed {
  [prop: string]:
  | number
  | string
  | (<K extends keyof this,
    T extends {
      [key in K]: number
    }>(props: T) => void)
}

type Values<T> = T[keyof T]

class A {
  [prop: string]:
  | number
  | string
  | (<K extends keyof this,
    T extends {
      [key in K]: number
    }>(props: T) => void)

  animate<
    Keys extends PropertyKey,
    Value extends number,
    Props extends Record<Keys, Value>
  >(props: Props) {

    const initialValue: Indexed = {}
    const changeValue: Indexed = {}

    for (let key in props) {
      initialValue[key] = this[key]
      changeValue[key] = props[key] - (this[key] as number)

      // ...
    }
  }
}

class B extends A {
  width: number
  heigth: number
  name: string

  constructor() {
    super();
    this.heigth = 20
    this.width = 20
    this.name = 'B'
  }
}
const b = new B();

const result = b.animate({
  width: 40,
  heigth: 40
})

Playground

P.S. Try to avoid mutations in TypeScript.

See my article and related answers: [ first, second, third, TS improvement ]