Question
In TypeScript, how can you make a function accept a key of an object and ensure that the value parameter matches the type of that specific property?
For example:
type JWT = { id: string; token: string; expire: Date };
const obj: JWT = {
id: 'abc123',
token: 'tk01',
expire: new Date(2018, 2, 14)
};
function print(key: keyof JWT) {
switch (key) {
case 'id':
case 'token':
console.log(obj[key].toUpperCase());
break;
case 'expire':
console.log(obj[key].toISOString());
break;
}
}
function onChange(key: keyof JWT, value: any) {
switch (key) {
case 'id':
case 'token':
obj[key] = value + ' (assigned)';
break;
case 'expire':
obj[key] = value;
break;
}
}
print('id');
print('expire');
onChange('id', 'def456');
onChange('expire', new Date(2018, 3, 14));
print('id');
print('expire');
onChange('expire', 1337); // this should fail at compile time
print('expire'); // otherwise it fails later at runtime
The goal is for the value parameter to depend on the key parameter, so that passing 'expire' requires a Date, while passing 'id' or 'token' requires a string.
What TypeScript feature should be used instead of any to express “the value type for the given key”?
Short Answer
By the end of this page, you will understand how to make one function parameter depend on another in TypeScript. You will learn how keyof, generics, and indexed access types like T[K] work together so that a key such as 'expire' automatically requires a Date, while 'id' requires a string.
Concept
TypeScript does not have a built-in keyword called valueof that mirrors keyof in the way beginners often expect. Instead, the usual solution is indexed access types.
If you have a type:
type JWT = {
id: string;
token: string;
expire: Date;
};
then:
type ExpireType = JWT['expire']; // Date
type IdType = JWT['id']; // string
That is how you ask TypeScript for the type of a property value.
The important next step is making that depend on a function argument. For that, you use a generic type parameter:
function onChange<K extends keyof JWT>(key: K, value: JWT[K]) {
obj[key] = value;
}
Here is what this means:
Mental Model
Think of an object type as a labeled cabinet:
keyof JWTgives you the list of drawer labels:id,token,expireJWT[K]means: “open the drawer namedKand tell me what kind of thing belongs inside”
So if the label is:
id→ the drawer holdsstringtoken→ the drawer holdsstringexpire→ the drawer holdsDate
A generic function lets TypeScript remember which drawer label you passed in, so it can enforce the correct type for the value going into that drawer.
Syntax and Examples
Core syntax
function update<K extends keyof T>(key: K, value: T[K]) {
// ...
}
This pattern means:
keymust be a valid key fromTvaluemust match the type of that key
Example with JWT
type JWT = {
id: string;
token: string;
expire: Date;
};
const obj: JWT = {
id: 'abc123',
token: 'tk01',
expire: new Date(2018, 2, 14)
};
function onChange<K extends keyof JWT>(key: K, : [K]) {
obj[key] = value;
}
(, );
(, );
(, ());
(, );
Step by Step Execution
Example
type JWT = {
id: string;
token: string;
expire: Date;
};
const obj: JWT = {
id: 'abc123',
token: 'tk01',
expire: new Date('2018-03-14')
};
function onChange<K extends keyof JWT>(key: K, value: JWT[K]) {
obj[key] = value;
}
onChange('id', 'def456');
onChange('expire', new Date('2018-04-14'));
What happens step by step
Call 1
onChange('id', 'def456');
TypeScript infers:
Real World Use Cases
Updating form state
When building forms, different fields have different types:
type FormState = {
name: string;
age: number;
subscribed: boolean;
};
A typed update function can enforce correct values for each field.
Updating configuration objects
Application config often mixes strings, numbers, and booleans. Using T[K] prevents assigning the wrong type to a setting.
Working with API models
If you receive structured data from an API, typed key/value updates help keep model objects valid.
Generic utility functions
Libraries and shared utilities often need to read or write object properties safely without losing type information.
State management
In React, Vue, or other UI code, this pattern is useful when updating state by field name while keeping compile-time type safety.
Real Codebase Usage
In real projects, developers commonly combine indexed access types with a few patterns.
Generic property setters
function setProp<T, K extends keyof T>(obj: T, key: K, value: T[K]) {
obj[key] = value;
}
This works for many object types, not just JWT.
Validation before assignment
Sometimes the value is unknown at first, then checked before storing:
function updateExpire(value: unknown) {
if (!(value instanceof Date)) {
throw new Error('expire must be a Date');
}
obj.expire = value;
}
Static types help at compile time, and runtime validation helps when data comes from outside TypeScript.
Guard clauses
Developers often split logic for readability:
function print(key: keyof JWT) {
if (key === ) {
.(obj[key].());
;
}
.(obj[key].());
}
Common Mistakes
Using any
function onChange(key: keyof JWT, value: any) {
obj[key] = value;
}
Why it is a problem
any disables type safety. TypeScript will allow wrong assignments like:
onChange('expire', 1337);
Fix
Use a generic and indexed access type:
function onChange<K extends keyof JWT>(key: K, value: JWT[K]) {
obj[key] = value;
}
Using JWT[keyof JWT] for the value
function onChange(key: keyof JWT, value: JWT[keyof JWT]) {
obj[key] = value;
}
Why it is wrong here
becomes:
Comparisons
Related type patterns
| Pattern | Meaning | Example result with JWT | Best use |
|---|---|---|---|
keyof JWT | Union of keys | `'id' | 'token' |
JWT['id'] | Value type for one known key | string | Get one property type |
JWT[K] | Value type for a generic key | depends on K | Functions where value depends on key |
JWT[keyof JWT] | Union of all value types | `string | Date` |
Cheat Sheet
// Keys of an object type
keyof T
// Value type of one property
T['name']
// Value type for a generic key
T[K]
// Generic key/value function
function setProp<T, K extends keyof T>(obj: T, key: K, value: T[K]) {
obj[key] = value;
}
// Union of all value types
type ValueOf<T> = T[keyof T];
Rules to remember
keyof Tgives property namesT[K]gives the value type for keyK- Use
K extends keyof Tto restrict valid keys - Use
value: T[K]when the value type depends on the key T[keyof T]is a union of all value types, not the exact value for one key- Avoid
anyif you want compile-time safety
Example
function onChange<K extends keyof JWT>(key: K, value: [K]) {
obj[key] = value;
}
FAQ
Is there a built-in valueof in TypeScript?
No. The usual equivalent idea is an indexed access type such as T[K] or T[keyof T].
How do I get the type of a property by key in TypeScript?
Use indexed access syntax:
type NameType = User['name'];
How do I make one function parameter depend on another in TypeScript?
Use a generic key parameter:
function f<K extends keyof T>(key: K, value: T[K]) {}
What is the difference between T[K] and T[keyof T]?
T[K] is the value type for one specific key. T[keyof T] is the union of all property value types.
Why does any cause problems here?
Because any disables type checking, so TypeScript cannot stop invalid assignments like putting a number into a property.
Mini Project
Description
Build a type-safe profile updater for a user settings object. The goal is to practice linking a property key to the correct value type so that invalid updates are rejected at compile time.
Goal
Create a reusable update function that only accepts values matching the selected object key.
Requirements
- Define a
UserSettingstype with at least onestring, onenumber, and onebooleanproperty. - Create a
settingsobject that matches the type. - Write a generic
updateSettingfunction usingK extends keyof UserSettingsandUserSettings[K]. - Demonstrate at least three valid updates.
- Include one invalid update that TypeScript should reject.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.
Angular Dependency Injection: Fix "Can't Resolve All Parameters for Component" Errors
Learn why Angular shows "Can't resolve all parameters for component" and how to fix service injection issues in components.