Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a `valueof` similar to `keyof` in TypeScript?

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?

like image 867
styfle Avatar asked Mar 14 '18 19:03

styfle


People also ask

What is Keyof typeof in TypeScript?

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 do you get the keys of a type in TypeScript?

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 .

What is type assertion in TypeScript?

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.

What is typeof in TypeScript?

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.


2 Answers

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.

like image 123
jcalz Avatar answered Sep 19 '22 18:09

jcalz


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

like image 27
Chris Kowalski Avatar answered Sep 18 '22 18:09

Chris Kowalski