Reputation: 1167
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]'.
}
}
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
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
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