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?
The keyof type operatorThe keyof operator takes an object type and produces a string or numeric literal union of its keys. The following type P is the same type as “x” | “y”: type Point = { x : number; y : number }; type P = keyof Point ; type P = keyof Point.
How to get the keys of a TypeScript interface? To get the union of the keys of a TypeScript interface, we can use the keyof keyword. interface Person { name: string; age: number; location: string; } type Keys = keyof Person; to create the Keys type by setting it to keyof Person .
In Typescript, Type assertion is a technique that informs the compiler about the type of a variable. Type assertion is similar to typecasting but it doesn't reconstruct code. You can use type assertion to specify a value's type and tell the compiler not to deduce it.
TypeScript adds a typeof operator you can use in a type context to refer to the type of a variable or property: let s = "hello"; let n : typeof s ; let n: string. This isn't very useful for basic types, but combined with other type operators, you can use typeof to conveniently express many patterns.
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.
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 = { a: { type: 'Reset' data: number } b: { type: 'Apply' data: string } } type actionValues = valueof<actions>
Works as expected :) Returns an Union of all possible types
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With