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(),
}
}