Given the function below, I would like to be able to create a type guard for an object/array, indicating whether it has an index or not, whether it's a property or an index. Is this possible in any way?
export function hasIndex(v: unknown, i = 0): v is [typeof keyof i] {
const p = (v as Record<PropertyKey, unknown>)?.[i];
return typeof p === 'number' || p instanceof Number;
}
Usage:
export function extractIndex(v: unknown, i = 0) {
let x = 0;
if (hasIndex(v, i)) {
x = v[i]; // <-- e.g.: `v[i]` should be `number`, not `number | undefined`
}
return x;
}
SOLVED:
Thanks, guys, @jcalz and @Alexander Nenashev. I really like the way you both think! I hadn’t used as
this way in arguments before—it didn’t even cross my mind.
The detail about extractIndex<K extends number>
really addressed the point @jcalz mentioned earlier. But honestly, I’m not sure I completely understand. Since the default value is a number, shouldn’t TypeScript infer it? Either way, thanks a lot! I was having a bit of trouble finding this solution, but "once I learned about it, it was so obvious".
{ [i in K]: number }
haha
Given the function below, I would like to be able to create a type guard for an object/array, indicating whether it has an index or not, whether it's a property or an index. Is this possible in any way?
export function hasIndex(v: unknown, i = 0): v is [typeof keyof i] {
const p = (v as Record<PropertyKey, unknown>)?.[i];
return typeof p === 'number' || p instanceof Number;
}
Usage:
export function extractIndex(v: unknown, i = 0) {
let x = 0;
if (hasIndex(v, i)) {
x = v[i]; // <-- e.g.: `v[i]` should be `number`, not `number | undefined`
}
return x;
}
SOLVED:
Thanks, guys, @jcalz and @Alexander Nenashev. I really like the way you both think! I hadn’t used as
this way in arguments before—it didn’t even cross my mind.
The detail about extractIndex<K extends number>
really addressed the point @jcalz mentioned earlier. But honestly, I’m not sure I completely understand. Since the default value is a number, shouldn’t TypeScript infer it? Either way, thanks a lot! I was having a bit of trouble finding this solution, but "once I learned about it, it was so obvious".
{ [i in K]: number }
haha
Share Improve this question edited Mar 6 at 20:51 Adrian Miranda asked Mar 5 at 22:42 Adrian MirandaAdrian Miranda 3384 silver badges13 bronze badges 2 |1 Answer
Reset to default 1My guess when we change i
from number
to K extends number
we are telling TS that we would use a specific number
subtype instead of generic number
so TS could actually narrow v
to a type that contains a specific index with number
value. (I don't read @jcalz's comments under questions and learn from answering myself):
Playground
export function hasIndex<K extends number>(v: unknown, i = 0 as K ): v is {[k in K]: number} {
const p = (v as Record<K, unknown>)?.[i];
return typeof p === 'number' || p instanceof Number;
}
export function extractIndex<K extends number>(v: unknown, i = 0 as K) {
let x = 0;
if (hasIndex(v, i)) {
x = v[i]; // <-- e.g.: `v[i]` should be `number`, not `number | undefined`
const y = v[1213]; // error
}
return x;
}
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745004199a4605677.html
i
is just of typenumber
(without necessarily narrowingv
toRecord<number, number>
which is presumably not your intent). You needi
to be something specific like0
or6
or something generic likeK extends number
, but for justnumber
it won't do what you want. Even regular control flow analysis has only recently supported this sort of "identical key of some wide type", see microsoft/TypeScript#57847, but it's something you can write as a type predicate. – jcalz Commented Mar 5 at 23:04