Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How can I move a generic TS argument definition without getting the "Property doesn't exist on type 'unknown'" error?

I’m confused as to why the definitions of doSomething1() and doSomething2() are not treated equally?

class Example<
    TypeA = void,
    TypeB = void,
    ArgsType = { valueA?:TypeA, valueB?:TypeB }
> {
    valueA?:TypeA;
    valueB?:TypeB;
    
    constructor( valueA:TypeA, valueB:TypeB ) {
        this.valueA = valueA;
        this.valueB = valueB;
    }
    
    doSomething1( args: { valueA?:TypeA, valueB?:TypeB } ) {
        // this works
        const { valueA, valueB } = args;
        
        console.log( valueA );
        console.log( valueB );
    }
    
    doSomething2( args: ArgsType ) {
        // this throws "Property 'valueA' does not exist on type 'unknown'"
        const { valueA, valueB } = args;
        
        console.log( valueA );
        console.log( valueB );
    }
    
}

If I define all the parameters manually (seen in doSomething1()), the code works as expected. If I try to stay DRY and move it next to where TypeA and TypeB are defined, creating a new generic type for the arguments, I get a Property 'valueA' does not exist on type 'unknown'.

It seems to be related to the TypeA and TypeB generics being optional, but that’s still the case when I’m using them manually, so what’s happening here?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

How can I define ArgsType derived from optional generics and re-use it in my method definitions?

>Solution :

The logic behind this one is that you can have {valueA: TypeA, valueB: TypeB} in ArgsType, but you could also have {whatever: 999, somethingElse: "Hello"} instead.

The compiler is right.

To define it you could do:

interface ArgsType<A, B> {
  valueA: A;
  valueB: B;
}

class Example<TypeA = void, TypeB = void, T extends ArgsType<TypeA, TypeB> = ArgsType<TypeA, TypeB>> {

  valueA: TypeA;
  valueB: TypeB;

  constructor(a: TypeA, b: TypeB) {
    this.valueA = a;
    this.valueB = b;
  }

  doSomething1(args: {valueA?: TypeA, valueB?: TypeB}) {
    const { valueA, valueB } = args;
    
    console.log( valueA );
    console.log( valueB );
  }

  doSomething2(args: T) { // T extends ArgsType<TypeA, TypeB>
    const { valueA, valueB } = args;
    
    console.log( valueA );
    console.log( valueB );
  }
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading