Home > Mobile >  Type 'key' cannot be used to index type 'P', when key is keyof 'PS' wh
Type 'key' cannot be used to index type 'P', when key is keyof 'PS' wh

Time:04-05

typescript claims for error when actually I claim this should not be an error and a limitation of typescript in the understanding of my case, or maybe I'm not writing it right?

here we declare interface Feature which uses 2 generics, P which will represent props and PS which represents pre-parsed props. parsed props keys is always subset of props keys.

export type Feature<
    P extends any,
    PS extends { [key in keyof P]?: any } = {}, // all keys in PS must be in P
    > = {
    props: P;
    parseProps?: {
        [key in keyof PS]: (prop: P[key]) => PS[key]; 
                                //^ this is error when it should not be error
                                //  key in PS must be also in P, so i can use key to index P, but this is error
    };
};

// just example for reference, this is not required to understand the question
// this example working and the types are correct, but we have error in the declaretion of type `Feature`
const feature1:Feature<{age:number,somethingElse:any},{age:string}> = {
    props: {
        age: 1,
        somethingElse:"bla"
    },
    parseProps: {
        age: (age) => age.toString()
            //^ age:number as declared in P    ^ return type of type 'string' as declared in PS
    }
}


CodePudding user response:

PS extends { [key in keyof P]?: any } means that PS has at least the keys of P, but can have more keys. So "key in PS must be also in P" is false.

See Playground.

CodePudding user response:

PS may have more keys than P so keyof PS is broader than keyof P.

Eliminate the potential extra keys in keyof PS to match the union keyof P:

/**
 * Example: 
 * P = {age:number,somethingElse:any}
 * PS = {age:string}
 * 
 * Inner Exclude = Exclude<'age' | 'somethingElse, 'age'> = 'somethingElse';
 * Outer Exclude = Exclude<'age' | 'somethingElse, 'somethingElse'> = 'age' = keyof P ∩ keyof PS;
 */
type PickValidKeys<P, PS> = Exclude<keyof P, Exclude<keyof P, keyof PS>>

export type Feature<
    P extends Record<string, any>,
    PS extends Partial<{ [key in keyof P]: any }>, // all keys in PS must be in P
> = {
    props: P;
    parseProps?: {
        [key in PickValidKeys<P, PS>]: (prop: P[key]) => PS[key]; 
    };
};

const feature1:Feature<{age:number,somethingElse:any},{age:string}> = {
    props: {
        age: 1,
        somethingElse:"bla"
    },
    parseProps: {
        age: (age: number) => age.toString(),
    }
}

See playground

  • Related