Home > Blockchain >  How can I use an interface extension as a parameter and test for an extra property?
How can I use an interface extension as a parameter and test for an extra property?

Time:05-04

Consider the following code (playground link here).

interface Person {
    name: string;
}
interface Child extends Person {
    primaryContactParent: string;
}
const sampleFunctionWrapper = function(child: Child) {
    return sampleFunction(child); //works fine
}
const sampleFunction = function<P extends Person>(person: P) {
    if(typeof person.primaryContactParent !== 'undefined') {
        console.log('Primary contact parent is '   person.primaryContactParent);
    }
    if('primaryContactParent' in person) {
        console.log('Primary contact parent is '   person.primaryContactParent);
    }
}

The repeated error is TS(2339): Property 'primaryContactParent' does not exist on type 'P'.

The parameter type P extends Person but the type checker won't permit even checking for the presence of any field not defined on Person itself. I could certainly understand not guaranteeing the presence of anything not defined on Person, but to not even be able to check for the field of something which might extend it has me a bit puzzled. In what way am I misunderstanding the possibilities of using one interface that extends another?

CodePudding user response:

To get this working you need to specify P to extend Person or Child.

const sampleFunction = function<P extends Person | Child>(person: P) {
    if('primaryContactParent' in person) {
        console.log('Primary contact parent is '   person.primaryContactParent);
    }
}

With a constraint like extends Person we signal the function that it can expect a type with a capabilities of Person and the type system will not allow to access anything outside of these capabilities.

CodePudding user response:

One method you can use to get type safety here is to create a helper function to check for the existence of the property. This way you can type the helper function so that it will inform TypeScript of that prop's existence via a type guard.

Eg, something like:

function hasProp<P extends string>(obj: {}, prop: P): obj is Record<P, unknown> {
    return prop in obj;
}

Then, if you use that function to check instead, the property will exist and will have the type unknown inside of the if block's scope:

const sampleFunction = function<P extends Person>(person: P) {
    if (hasProp(person, "primaryContactParent")) {
        console.log("Primary contact parent is "   person.primaryContactParent); // No error.
    }
}

Typescript Playground

  • Related