Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
I want to be able to assign an object property to a value given a key and value as inputs yet still be able to determine the type of the value. It's a bit hard to explain so this code should reveal the problem:
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); // should fail here at compile time
print('expire'); // actually fails here at run time
I tried changing value: any
to value: valueof JWT
but that didn't work.
Ideally, onChange('expire', 1337)
would fail because 1337
is not a Date type.
How can I change value: any
to be the value of the given key?
–
UPDATE: Looks like the question title attracts people looking for a union of all possible property value types, analogous to the way keyof
gives you the union of all possible property key types. Let's help those people first. You can make a ValueOf
analogous to keyof
, by using indexed access types with keyof T
as the key, like so:
type ValueOf<T> = T[keyof T];
which gives you
type Foo = { a: string, b: number };
type ValueOfFoo = ValueOf<Foo>; // string | number
For the question as stated, you can use individual keys, narrower than keyof T
, to extract just the value type you care about:
type sameAsString = Foo['a']; // look up a in Foo
type sameAsNumber = Foo['b']; // look up b in Foo
In order to make sure that the key/value pair "match up" properly in a function, you should use generics as well as indexed access types, like this:
declare function onChange<K extends keyof JWT>(key: K, value: JWT[K]): void;
onChange('id', 'def456'); // okay
onChange('expire', new Date(2018, 3, 14)); // okay
onChange('expire', 1337); // error. 1337 not assignable to Date
The idea is that the key
parameter allows the compiler to infer the generic K
parameter. Then it requires that value
matches JWT[K]
, the indexed access type you need.
–
–
–
–
There is another way to extract the union type of the object:
const myObj = { a: 1, b: 'some_string' } as const;
type values = typeof myObj[keyof typeof myObj];
Result: 1 | "some_string"
–
–
–
If anyone still looks for implementation of valueof
for any purposes, this is a one I came up with:
type valueof<T> = T[keyof T]
Usage:
type actions = {
type: 'Reset'
data: number
type: 'Apply'
data: string
type actionValues = valueof<actions>
Works as expected :) Returns an Union of all possible types
With the function below you can limit the value to be the one for that particular key.
function setAttribute<T extends Object, U extends keyof T>(obj: T, key: U, value: T[U]) {
obj[key] = value;
Example
interface Pet {
name: string;
age: number;
const dog: Pet = { name: 'firulais', age: 8 };
setAttribute(dog, 'name', 'peluche') <-- Works
setAttribute(dog, 'name', 100) <-- Error (number is not string)
setAttribute(dog, 'age', 2) <-- Works
setAttribute(dog, 'lastname', '') <-- Error (lastname is not a property)
// type TEST1 = boolean | 42 | "heyhey"
type TEST1 = ValueOf<{ foo: 42, sort: 'heyhey', bool: boolean }>
// type TEST2 = 1 | 4 | 9 | "zzz..."
type TEST2 = ValueOf<[1, 4, 9, 'zzz...']>
–
You can made a Generic for your self to get the types of values, BUT, please consider the declaration of object should be declared as const
, like:
export const APP_ENTITIES = {
person: 'PERSON',
page: 'PAGE',
} as const; <--- this `as const` I meant
Then the below generic will work properly:
export type ValueOf<T> = T[keyof T];
Now use it like below:
const entity: ValueOf<typeof APP_ENTITIES> = 'P...'; // ... means typing
// it refers 'PAGE' and 'PERSON' to you
Thanks the existing answers which solve the problem perfectly. Just wanted to add up a lib has included this utility type, if you prefer to import this common one.
https://github.com/piotrwitek/utility-types#valuestypet
import { ValuesType } from 'utility-types';
type Props = { name: string; age: number; visible: boolean };
// Expect: string | number | boolean
type PropsValues = ValuesType<Props>;
–
You could use help of generics to define T
that is a key of JWT and value to be of type JWT[T]
function onChange<T extends keyof JWT>(key: T, value: JWT[T]);
the only problem here is in the implementation that following obj[key] = value + ' (assigned)';
will not work because it will try to assign string
to string & Date
. The fix here is to change index from key
to token
so compiler knows that the target variable type is string
.
Another way to fix the issue is to use Type Guard
// IF we have such a guard defined
function isId(input: string): input is 'id' {
if(input === 'id') {
return true;
return false;
// THEN we could do an assignment in "if" block
// instead of switch and compiler knows obj[key]
// expects string value
if(isId(key)) {
obj[key] = value + ' (assigned)';
with type-fest lib, you can do that with ValueOf
like that:
import type { ValueOf } from 'type-fest';
export const PATH_NAMES = {
home: '/',
users: '/users',
login: '/login',
signup: '/signup',
interface IMenu {
id: ValueOf<typeof PATH_NAMES>;
label: string;
onClick: () => void;
icon: ReactNode;
const menus: IMenu[] = [
id: PATH_NAMES.home,
label: t('common:home'),
onClick: () => dispatch(showHome()),
icon: <GroupIcon />,
id: PATH_NAMES.users,
label: t('user:users'),
onClick: () => dispatch(showUsers()),
icon: <GroupIcon />,
I realize this is slightly off topic, That said every time I've looked for a solution to this. I get sent to this post. So for those of you looking for String Literal Type generator, here you go.
This will create a string Literal list from an object type.
export type StringLiteralList<T, K extends keyof T> = T[keyof Pick<T, K>];
type DogNameType = { name: "Bob", breed: "Boxer" } | { name: "Pepper", breed: "Spaniel" } | { name: "Polly", breed: "Spaniel" };
export type DogNames = StringLiteralList<DogNameType, "name">;
// type DogNames = "Bob" | "Pepper" | "Polly";
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.