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.