Reputation: 4194
I have a base class:
class ClassA {
public prop1: string;
}
I have a generic interface:
interface IMyInterface<T extends ClassA> {
objA: T;
}
And a generic class:
class MyClass<T extends ClassA> implements IMyInterface<T> {
public objA: T;
public myMethod () {
this.objA.prop1 = "foo"; // This is ok
if ("prop2" in this.objA) {
this.objA.prop2 = "bar"; // This is not
}
}
}
How can I force the prop2
in objA
if, and only if, this property exists in objA
?
Do I have to force a cast as (this.objA as ClassA & {prop2: string}).prop2 = "bar"
?
Upvotes: 0
Views: 1103
Reputation: 46
You can use type guards, as described here, inside the Type Guards and Differentiating Types
session
Your code would be something like this:
class ClassA {
public prop1: string;
}
interface IMyInterface<T extends ClassA> {
objA: T;
}
interface IProp2 {
prop2: string;
}
function hasProp2(obj: ClassA | IProp2): obj is IProp2 {
return (<IProp2>obj).prop2 !== undefined;
}
class MyClass<T extends ClassA> implements IMyInterface<T> {
public objA: T;
public myMethod () {
this.objA.prop1 = "foo"; // This is ok
if (hasProp2(this.objA)){
this.objA.prop2 = "bar"; // Now this is ok
}
}
}
Upvotes: 1
Reputation: 32266
This appears to be a current limitation of Typescript. I found this issue describing this exact case.
The workaround is the same as what you've already mentioned. You'll have to cast this.objA
to some other type that permits setting a prop2
field. So:
(this.objA as ClassA & {prop2: string}).prop2 = "bar"
// Or to trade some safety for brevity:
(this.objA as any).prop2 = "bar"
Otherwise, you can also use a custom type guard to assert the existence of that key. Using the one from the issue linked above, you can do something like this:
function inOperator<K extends string, T>(k: K, o: T): o is T & Record<K, any> {
return k in o;
}
Then, using that instead of the in
operator:
if (inOperator("prop2", this.objA)) {
this.objA.prop2 = "bar"; // No error now
}
Upvotes: 2