Issue
I am trying to write some data validation helpers that assure the TypeScript compiler that
function process(data: unknown) {
assert(data instanceof Object);
// need a helper for this line
assert( hasSubobject(data, 'some_field') );
// so that this line doesn't complain about `some_field` not existing
assert(typeof data.some_field === 'string');
// …
}
I know I could just hardcode this as 'some_field' in data && data.some_field instanceof Object
inline and even figured out that I could hardcode a helper like:
function hasHardcodedSubobject(data: object): data is {hardcodedField: object} {
return (
"hardcodedField" in data &&
data.hardcodedField instanceof Object
);
}
but I am unsure of how to convert this to something more dynamic, like:
function hasSubobject(data: object, key: string): data is {[key]: object} {
return (
key in data &&
data[key] instanceof Object
);
}
This is somewhat similar to https://stackoverflow.com/a/68959950/179583 and in that case the solution could just use "key" as a variable via key is keyof T
but for some reason I can't use it my way. It gives:
A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1170)
even though with key: string
its type is a literal (update: I'm probably mixing up "literal" vs. "primitive"!) type, i.e. string
?
Solution
You need generic types on your hasSubobject
function. One to capture the object type you are passing in, and another for the key your validating.
function hasSubobject<T extends object, K extends string>(
data: T,
key: K
): data is T & { [key in K]: object } {
return (
key in data &&
data[key as unknown as keyof T] instanceof Object
);
}
Here T
is the object type being validated, and K
os the key name that will be narrowed.
The return value is then a type predicate where T
is intersected with an object type where the property K
is locked down to be an object
type.
This data[key as unknown as keyof T]
is unfortunate, but Typescript can't seem to track that key
is a keyof T
, but it definitely is due to the key in data
check. So we can override it with the forceful assertion here.
Answered By - Alex Wayne
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.