patterntypescriptCritical
Is there a `valueof` similar to `keyof` in TypeScript?
Viewed 0 times
typescriptkeyofsimilarvalueofthere
Problem
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:
I tried changing
Ideally,
How can I change
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 timeI 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?Solution
UPDATE: Looks like the question title attracts people looking for a union of all possible property value types, analogous to the way
which gives you
ALSO: If you're creating an object literal and need the union of those property values as literal types like
For the question as stated, you can use individual keys, narrower than
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:
The idea is that the
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[keyof T];which gives you
type Foo = { a: string, b: number };
type ValueOfFoo = ValueOf; // string | numberALSO: If you're creating an object literal and need the union of those property values as literal types like
"0" instead of string, then you might want to use a const assertion; see @Dima's answer.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 FooIn 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(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 DateThe 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.Code Snippets
type ValueOf<T> = T[keyof T];type Foo = { a: string, b: number };
type ValueOfFoo = ValueOf<Foo>; // string | numbertype sameAsString = Foo['a']; // look up a in Foo
type sameAsNumber = Foo['b']; // look up b in Foodeclare 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 DateContext
Stack Overflow Q#49285864, score: 840
Revisions (0)
No revisions yet.