Reputation: 559
I have this chunk of Typescript code :
interface UnknownParams { [key: string]: any };
interface ComponentParams {
foo: string;
bar: string;
}
interface IComponent<ParamsType> {
params: ParamsType;
}
type ComponentConstructor<T> = (new (params: T) => IComponent<T>) & {
something: boolean;
doAnything: (constructor: ComponentConstructor<UnknownParams>) => void;
};
const myComponentConstructor: ComponentConstructor<ComponentParams> = class {
constructor(params: ComponentParams) {
this.params = params;
}
params: ComponentParams;
static something: boolean;
static doAnything(constructor: ComponentConstructor<UnknownParams>) {
constructor.something = true;
}
}
myComponentConstructor.doAnything(myComponentConstructor);
On the last line I have this error :
const myComponentConstructor: ComponentConstructor<ComponentParams>
Argument of type 'ComponentConstructor<ComponentParams>' is not assignable to parameter of type 'ComponentConstructor<UnknownParams>'.
Type 'ComponentConstructor<ComponentParams>' is not assignable to type 'new (params: UnknownParams) => IComponent<UnknownParams>'.
Types of parameters 'params' and 'params' are incompatible.
Type 'UnknownParams' is missing the following properties from type 'ComponentParams': foo, bar — ts(2345)
I don't understand why my ComponentConstructor<ComponentParams>
in not assignable to ComponentConstructor<UnknownParams>
, cause any object could be assigned to { [key: string]: any }
, insn't it ?
EDIT :
After @DaGardner's answer, I found a solution that is to make doAnything
a generic function with a type parameter instead of UnknownParams
, but in my case, that function is part of a class as you can see in the code above.
Do I have to add the type parameter to the class like ComponentConstructor<ComponentParams, DoAnythingTypeParameter>
, or is there another solution that could solve this problem ?
Thanks
Upvotes: 0
Views: 433
Reputation: 6994
The relation is the other way around. You can see it if you try to do something in doAnything
:
function doAnything(constructor: ComponentConstructor<UnknownParams>){
return new constructor({ test: 3 });
};
this compiles just fine - as it should, since the constructor accepts, as you said, any object.
If we pass myComponent
to doAnything
myComponent
cannot take { test: 3 }
and therefore cannot be passed to doAnything
.
If this would be this way around:
interface UnknownParams { [key: string]: any };
interface ComponentParams {
foo: string;
bar: string;
}
type ComponentConstructor<T> = new (params: T) => any;
function doAnything(constrcutor: ComponentConstructor<ComponentParams>){ /* */};
const myComponent: ComponentConstructor<UnknownParams> = class {
constructor(params: UnknownParams) {/* ... */}
}
doAnything(myComponent);
It compiles, as the constructor of myComponent
is less restrictive than the signature of doAnything
requires.
I'm not entirely sure what you are trying to achieve but, you can fix the produced typescript errors pretty easily by reusing the generic type off ComponentConstructor
type ComponentConstructor<T> = (new (params: T) => IComponent<T>) & {
something: boolean;
doAnything: (constructor: ComponentConstructor<T>) => void;
};
// and then adapting the implementation as well, note the replaced UnknownProps with the actual ComponentProps
const myComponentConstructor: ComponentConstructor<ComponentParams> = class {
constructor(params: ComponentParams) {
this.params = params;
}
params: ComponentParams;
static something: boolean;
static doAnything(constructor: ComponentConstructor<ComponentParams>) {
constructor.something = true;
}
}
Upvotes: 1