keyof T is the union of the property names of T. For { sku: string, name: string, price: number }, keyof gives "sku" | "name" | "price", so a parameter typed {keyof Product} accepts only those strings and rejects a typo like "prise" (TS2345).
For the keys of an object you have as a value, not as a type, combine it with typeof: {keyof typeof LABELS}.
At a glance
| Use | JSDoc in a .js file | TypeScript in a .ts file |
|---|---|---|
| The keys of a type | /** @param {keyof Product} field */ | (field: keyof Product) |
| The keys of a value | /** @typedef {keyof typeof LABELS} Column */ | type Column = keyof typeof LABELS; |
Examples
/** @typedef {{ sku: string, name: string, price: number }} Product */
/**
* @param {Product[]} products
* @param {keyof Product} field
*/
export function sortBy(products, field) {
return [...products].sort((a, b) => String(a[field]).localeCompare(String(b[field])));
}
/** @type {Product[]} */
const products = [{ sku: "TEE-01", name: "Cotton tee", price: 19.5 }];
sortBy(products, "name");type Product = { sku: string; name: string; price: number };
export function sortBy(products: Product[], field: keyof Product) {
return [...products].sort((a, b) => String(a[field]).localeCompare(String(b[field])));
}
const products: Product[] = [{ sku: "TEE-01", name: "Cotton tee", price: 19.5 }];
sortBy(products, "name");jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} /** @typedef {{ sku: string, name: string, price: number }} Product */
/** @param {keyof Product} field */
export function column(field) {
return field;
}
column("prise");error TS2345: Argument of type '"prise"' is not assignable to parameter of type 'keyof Product'.jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} const LABELS = {
sku: "SKU",
name: "Product name",
price: "Price",
};
/** @typedef {keyof typeof LABELS} Column */
/** @param {Column} column */
export function heading(column) {
return LABELS[column];
}
heading("price");
heading("stock");error TS2345: Argument of type '"stock"' is not assignable to parameter of type '"sku" | "name" | "price"'.jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} With generics
keyof is most useful as a constraint: @template T with @template {keyof T} K lets a function take an object and one of its keys, and return T[K], the type of that property. The generic constraints page has that example.
Types with index signatures
For Record<string, number>, keyof is string (and number, since numeric keys are strings in JavaScript), so it no longer limits anything. keyof is precise only for types with known property names.
Gotchas
keyofof a union gives only the keys every member shares.- Inside the function,
a[field]has the union of all property types; narrow it or use the generic form when you need the exact one.
Questions
How do I accept only the property names of an object type in JSDoc?
Type the parameter as {keyof YourType}. For an object value, use {keyof typeof yourObject}.
Sources
- Keyof Type Operator · TypeScript handbook
Last reviewed 20 October 2026.