Terite
Terite

Reputation: 1167

Narrowing TypeScript index type

I have the following code that should compile, while it's not:

interface A {
    field: string;
    fieldB: { fieldC: string;}
}

function mixPropValue<P extends keyof A>(propName: P, value: A[P]) {
    if (propName === 'fieldB') {
        // value is always in type of { fieldC:string }
        value.fieldC; // Error: Property 'fieldC' does not exist on type 'A[P]'.
    }
}

Playground

Is it possible to let TypeScript know the type narrowing under first if branch? Of course I can get the code compiling using type casting, but I want to avoid it.

Upvotes: 0

Views: 740

Answers (2)

Fenton
Fenton

Reputation: 251062

You can narrow the type in the same hit as checking the property name in order to resolve the type to the narrower option:

interface A {
    field: string;
    fieldB: { fieldC: string;}
}

function isFieldB<P extends keyof A>(propName: P, prop: { fieldC: string } | any): prop is { fieldC: string } {
    return propName === 'fieldB';
}

function mixPropValue<P extends keyof A>(propName: P, value: A[P]) {
    if (isFieldB(propName, value)) {
        value.fieldC;
    }
}

A future version of the compiler may make this redundant, but it will be an impressive leap if they do it as there is a reasonable amount of "connecting the dots" here. The TypeScript team often manage it though.

Upvotes: 2

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250036

The compiler will not narrow types based on suck tests, you best bet is a custom type guard:

function isProp<P extends keyof A>(value: A[keyof A], targetProp: P, testProp : keyof A) : value is A[P] {
    return targetProp === testProp;
}
function mixPropValue<P extends keyof A>(propName: P, oldValue: A[P], newValue: A[P]) {
    if (isProp(oldValue, 'fieldB', propName) && isProp(newValue, 'fieldB', propName)) {
        // oldValue, newValue here are always in type of { fieldC:string }
        return { ...oldValue, ...newValue }; // Error: Spread types may only be created from object types.
    }
    return newValue;
}

Upvotes: 0

Related Questions