admin管理员组

文章数量:1406339

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 I don't think you can write a type guard that works for situations where i is just of type number (without necessarily narrowing v to Record<number, number> which is presumably not your intent). You need i to be something specific like 0 or 6 or something generic like K extends number, but for just number 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
  • (see prev comment) I suppose unless someone comes up with something very clever here I might write up an answer saying "this is not possible" with sources, but that's not much fun. – jcalz Commented Mar 5 at 23:05
Add a comment  | 

1 Answer 1

Reset to default 1

My 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;
}

本文标签: typescriptType guardindex of an objectStack Overflow