Reputation: 3964
I have a type guard, which sole purpose is to check the existence of property in object and that it has some value in it.
With type system I want to say this phrase to compiler, if type guard check succeeds:
This is an input object, if type guard succeeds then output is an object with lineHeight property
For exact object it looks like this:
type PickedLineHeight = Pick<TextStyle, "lineHeight">
type NonNullableLineHeight = Required<PickedLineHeight>
function hasLineHeight(style: object): style is NonNullableLineHeight {
return (
style.hasOwnProperty("lineHeight") &&
(style as PickedLineHeight).lineHeight !== undefined
)
}
How would I develop more generic version of function like hasProperty(style, prop)?
My attempt was:
function hasProperty<T extends { [key: string]: any}, U extends keyof T>(style: T, prop: U): style is Pick<T, U> {
return (
style.hasOwnProperty(prop) &&
style[prop] !== undefined
)
}
but I constantly get this error message, which I can't eliminate or understand
Upvotes: 1
Views: 225
Reputation: 328758
I'd probably type hasProperty()
this way:
function hasProperty<T extends object, K extends keyof T>(
style: T,
prop: K
): style is T & { [P in K]-?: Exclude<T[K], undefined> } {
return style.hasOwnProperty(prop) && style[prop] !== undefined;
}
This should reflect that hasProperty()
will verify that the property exists and is not undefined
. The guarded type, T & { [P in K]-?: Exclude<T[K], undefined> }
is assignable to T
(which is what the intersection T & ...
says) and has extra restrictions on the K
-keyed property. Note that { [P in K]-?: Exclude<T[K], undefined> }
could also be written Required<Record<K, Exclude<T[K], undefined>>
, which might be more understandable. Let's make sure it behaves as expected:
interface Example {
required: string;
optional?: string;
requiredButPossiblyUndefined: string | undefined;
requiredButPossiblyNull: string | null;
}
function checkExample(ex: Example) {
ex.required.toUpperCase(); // okay
// hasProperty de-optionalizes optional properties
ex.optional.toUpperCase(); // error, possibly undefined
if (hasProperty(ex, "optional")) {
ex.optional.toUpperCase(); // okay
}
// hasProperty removes undefined from list of possible values
ex.requiredButPossiblyUndefined.toUpperCase(); // error, possibly undefined
if (hasProperty(ex, "requiredButPossiblyUndefined")) {
ex.requiredButPossiblyUndefined.toUpperCase(); // okay
}
// hasProperty doesn't do anything with null
ex.requiredButPossiblyNull.toUpperCase(); // error, possibly null
if (hasProperty(ex, "requiredButPossiblyNull")) {
ex.requiredButPossiblyNull.toUpperCase(); // error, possibly null
}
}
Looks good. Okay, hope that helps. Good luck!
Upvotes: 1
Reputation: 3964
Accidentaly I came up to this soluton, which is for my opinion, is the same as which shows error.
But anyway, this is working without complains:
function hasProperty<T extends object>(style: T, prop: keyof T): style is Pick<T, typeof prop> {
return (
style.hasOwnProperty(prop) &&
style[prop] !== undefined
)
}
function hasFontSize(style: TextStyle) {
return hasProperty(style, "fontSize")
}
function hasLineHeight(style: TextStyle) {
return hasProperty(style, "lineHeight")
}
Upvotes: 0