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?
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 );
}
}