원클릭으로
avoid-numeric-index
Use when defining array-like types. Use when tempted to use number as index type. Use when understanding array keys.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Use when defining array-like types. Use when tempted to use number as index type. Use when understanding array keys.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Use when defining global types. Use when augmenting window. Use when typing environment variables. Use when working with build-time constants. Use when configuring type definitions.
Use when migrating JavaScript to TypeScript. Use when gradually adopting TypeScript. Use when working with mixed codebases. Use when converting large projects. Use when teams are learning TypeScript.
Use when writing asynchronous code. Use when tempted to use callbacks. Use when composing multiple async operations.
Use when creating types from example data. Use when types don't match all cases. Use when API responses vary.
Use when writing type annotations on variables. Use when TypeScript can infer the type. Use when code feels cluttered with types.
Use when functions have multiple parameters of same type. Use when parameter order is easy to confuse. Use when designing function signatures.
| name | avoid-numeric-index |
| description | Use when defining array-like types. Use when tempted to use number as index type. Use when understanding array keys. |
JavaScript object keys are always strings, even for arrays.
TypeScript's numeric index signatures are a helpful fiction for catching mistakes, but they don't reflect JavaScript's runtime behavior. Use Array, tuple, or ArrayLike types instead.
NEVER use number as an index signature type.
Use Array<T>, tuple, or ArrayLike<T> instead.
Remember:
Object.keys always returns strings// JavaScript converts numeric keys to strings
const obj = { 1: 'one', 2: 'two' };
console.log(Object.keys(obj)); // ['1', '2'] - strings!
// Arrays work the same way
const arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // ['0', '1', '2'] - strings!
// String access works on arrays
console.log(arr['1']); // 'b' - same as arr[1]
TypeScript pretends arrays have numeric indices:
interface Array<T> {
[n: number]: T; // Fiction: indices are actually strings
}
const xs = [1, 2, 3];
const x0 = xs[0]; // OK: number index
const x1 = xs['1']; // OK: TypeScript allows stringified numbers
// TypeScript catches non-numeric string indices
const inputEl = document.querySelector('input')!;
const bad = xs[inputEl.value];
// ~~~~~~~~~~~~~~~~~
// Index expression is not of type 'number'.
This is useful for catching mistakes, even though it's not technically accurate.
const xs = [1, 2, 3];
const keys = Object.keys(xs);
// ^? const keys: string[] (not number[]!)
for (const key of Object.keys(xs)) {
console.log(typeof key); // 'string', always
}
// This might make you think numeric keys are "real"
type NumericDict = { [key: number]: string };
// But at runtime:
const dict: NumericDict = { 0: 'zero', 1: 'one' };
console.log(Object.keys(dict)); // ['0', '1'] - strings!
// Instead of
type Bad = { [index: number]: string };
// Use
type Good = string[]; // or Array<string>
// Fixed-length numeric "index"
type Point = [number, number]; // 2 elements
type RGB = [number, number, number]; // 3 elements
const origin: Point = [0, 0];
// ArrayLike has length and numeric index signature
// Good for accepting any indexable collection
function sum(items: ArrayLike<number>): number {
let total = 0;
for (let i = 0; i < items.length; i++) {
total += items[i];
}
return total;
}
// Works with arrays
sum([1, 2, 3]);
// Works with array-like objects
sum({ 0: 1, 1: 2, 2: 3, length: 3 });
// Works with NodeList, arguments, etc.
// If you only need to iterate, not index
function sumIterable(items: Iterable<number>): number {
let total = 0;
for (const item of items) {
total += item;
}
return total;
}
// Works with arrays
sumIterable([1, 2, 3]);
// Works with Sets
sumIterable(new Set([1, 2, 3]));
// Works with generators
function* nums() { yield 1; yield 2; yield 3; }
sumIterable(nums());
// When you use a number index, you get T
// When you use Object.keys, you get string[]
// This inconsistency is intentional
const arr = [1, 2, 3];
// TypeScript: number index gives number
const first: number = arr[0]; // OK
// JavaScript reality: keys are strings
const keys = Object.keys(arr); // string[]
// You can use string indices (TypeScript allows numeric strings)
const second = arr['1']; // OK, TypeScript permits this
// Without noUncheckedIndexedAccess:
const arr = [1, 2, 3];
const x = arr[10];
// ^? const x: number (but actually undefined!)
// With noUncheckedIndexedAccess:
const y = arr[10];
// ^? const y: number | undefined (safer)
// Or use a wrapper function:
function checkedAccess<T>(xs: ArrayLike<T>, i: number): T {
if (i >= 0 && i < xs.length) {
return xs[i];
}
throw new Error(`Index ${i} out of bounds`);
}
Pressure: "I need { [n: number]: T } for a sparse array"
Response: Use Map<number, T> for sparse data, or regular arrays with optional access.
Action: new Map<number, string>() for sparse numeric keys.
Pressure: "My object uses IDs as keys: { 1: user1, 2: user2 }"
Response: Use Map<number, User> or Record<string, User> and convert.
Action: Embrace that keys are strings, or use Map.
{ [key: number]: T } in your own typesObject.keys(array) returns numberstypeof key is 'string'| Excuse | Reality |
|---|---|
| "Arrays have numeric indices" | At runtime, they're strings |
| "TypeScript uses number in Array" | It's a convenient fiction for type safety |
| "I need sparse numeric keys" | Use Map<number, T> instead |
// DON'T: Numeric index signature
type Bad = { [n: number]: string };
// DO: Use Array for sequences
type Good = string[];
// DO: Use tuple for fixed length
type Point = [number, number];
// DO: Use ArrayLike for indexable collections
function process(items: ArrayLike<string>) { ... }
// DO: Use Iterable for anything you can loop over
function process(items: Iterable<string>) { ... }
// DO: Use Map for sparse numeric keys
const sparse = new Map<number, string>();
Numeric index signatures are a TypeScript convenience, not JavaScript reality.
JavaScript object keys are always strings. TypeScript's numeric indices help catch mistakes but create a false mental model. Use Array, tuple, ArrayLike, or Map instead of defining your own numeric index signatures.
Based on "Effective TypeScript" by Dan Vanderkam, Item 17: Avoid Numeric Index Signatures.