JavaScript Is it possible for TypeScript to infer keys from a dynamic object?

(Assuming you use TS3.0 or greater in the following)

TypeScript supports the concept of string literal types as well as tuple types, so it is possible to get your value a to have the type ['ONE', 'TWO', 'THREE'] as well as the value ['ONE', 'TWO', 'THREE'], like this:

const a: ['ONE', 'TWO', 'THREE'] = ['ONE', 'TWO', 'THREE'];

(A less redundant way to get this to happen will come later):

Then you can represent the intended type of b as a mapping from keys to values that match the key exactly, using a mapped type:

type SameValueAsKeys<KS extends string[]> = { [K in KS[number]]: K };

which could be used like this:

const b: SameValueAsKeys<typeof a> = a.reduce((acc, type) => ({ ...acc, [type]: type }), {} as any);
b.ONE; // property type is "ONE"
b.TWO; // property type is "TWO"
b.THREE; // property type is "THREE"

Notice how the compiler knows that b has three keys, "ONE", "TWO", and "THREE", and that the values are the same as the keys.


So TypeScript certainly supports this type of dynamic typing. Unfortunately it's a bit tedious to use as I showed it above. One way to make this less annoying is to add some helper functions that allow the compiler to infer the proper types, or at least hide the type assertions in a library where the developer won't have to worry about them.

First, for a... the compiler doesn't infer tuple types, and it also tends to widen string literals to the string type except in certain instances. Let's introduce a helper function named stringTuple():

function stringTuple<T extends string[]>(...args: T) { return args; }

This infers a tuple type from rest arguments. Now we can use it to make a without typing redundant string values:

const a = stringTuple("ONE", "TWO", "THREE");

Next, let's introduce a function which takes a list of strings and returns an object whose keys are those strings and whose values match the strings:

function keyArrayToSameValueAsKeys<T extends string[]>(keys: T): SameValueAsKeys<T>;
function keyArrayToSameValueAsKeys(keys: string[]): { [k: string]: string } {
  return keys.reduce((acc, type) => ({ ...acc, [type]: type }), {});
}

Here we are using your same code with reduce, but we're hiding it inside its own function and using a single overload call signature to represent the intended output type. Now, we can get b like this:

const b = keyArrayToSameValueAsKeys(a);
b.ONE; // property type is "ONE"
b.TWO; // property type is "TWO"
b.THREE; // property type is "THREE"

If you put stringTuple() and keyArrayToSameValueAsKeys() in a library, the user can use them without too much trouble:

const otherObject = keyArrayToSameValueAsKeys(stringTuple("x", "y", "z"));
// const otherObject: {X: "X", Y: "Y", Z: "Z"}

Or you can merge them together like this:

function keysToSameValueAsKeys<T extends string[]>(...keys: T): { [K in T[number]]: K };
function keysToSameValueAsKeys(keys: string[]): { [k: string]: string } {
  return keys.reduce((acc, type) => ({ ...acc, [type]: type }), {});
}

And then get the output in one call, like this:

const lastOne = keysToSameValueAsKeys("tic", "tac", "toe");
// const lastOne: {tic: "tic", tac: "tac", toe: "toe"};

Okay, hope that's of some help. Good luck!

Answer:1

If you know in advance what will be in the array you can do something like this:

type myType = 'ONE' | 'TWO' | 'THREE';
const a: myType[] = ['ONE', 'TWO', 'THREE'];

const b: { [key in myType]: myType } = a.reduce<{ [key in myType]: myType }>((acc, type) => ({ ...acc, [type]: type }), <any>{});

// correct intellisense
b.ONE === 'ONE'
Answer:2

I've been trying to get this to work from other posts, but keep running into a wall. I have an "All" button, that I'd like select all or unselect all in the closest div class of "apply_all" I'm ...

I've been trying to get this to work from other posts, but keep running into a wall. I have an "All" button, that I'd like select all or unselect all in the closest div class of "apply_all" I'm ...

I just started with typescript and trying to create an instance to the typescript class but I am unsuccessful. Below are my files App.ts import { EventEmitter } from 'events'; interface Emitter ...

I just started with typescript and trying to create an instance to the typescript class but I am unsuccessful. Below are my files App.ts import { EventEmitter } from 'events'; interface Emitter ...

  1. typescript typeerror illegal constructor
  2. typescript typeerror not a constructor

I am trying to make this: 3 input boxes take the value of the input and compare to a certain percentage if values are within 3% of each other. output = something else output something else. any ...

I am trying to make this: 3 input boxes take the value of the input and compare to a certain percentage if values are within 3% of each other. output = something else output something else. any ...

  1. compare values within list python
  2. compare values within dictionary python
  3. compare values in excel
  4. compare values in two columns excel
  5. compare values in dictionary python
  6. compare values in array javascript
  7. compare values in same dictionary python
  8. compare values in two columns
  9. compare values in list python
  10. compare values in two dataframes pandas
  11. compare values in javascript
  12. compare values in sql
  13. compare values in two lists python
  14. compare values in two columns pandas
  15. compare values in two arrays javascript
  16. compare values in r
  17. compare values in python
  18. compare values in excel columns
  19. compare values in two sheets excel
  20. compare values in array

I'm currently working on developing some set of codes to display all blobs inside specified Azure Container using web front-end. I'm expecting the final output to be something like this: I started by ...

I'm currently working on developing some set of codes to display all blobs inside specified Azure Container using web front-end. I'm expecting the final output to be something like this: I started by ...