Search code examples
typescriptgenericskeyof

How can I specify that Typescript generic T[K] is number type?


I have a simple Typescript function like this:

function getProperty<T, K extends keyof T>(obj: T, key: K): number {
  return obj[key]; // This line is not compiling.
  // Typescript will yell: "Type 'T[K]' is not assignable to type 'number'."
}

My usage looks like this:

const someObj = {
  myValue: 123,
  otherProperty: '321'
}

getProperty(someObj, 'myValue')

I won't know what structure of someObj will be.

My question is: How can I specify that T[K] is number type statically?


Solution

  • I won't know what structure of someObj will be.

    Then TypeScript can't really help you with this. TypeScript's type checking is done at compile-time. If the structure of someObj will only be known at runtime, TypeScript can't make access to that structure typesafe. You'd need to know what the property keys and possible values for those properties are at compile-time.

    For instance: In your example, the property names are strings and the property values are either strings or numbers (but not booleans or objects, etc.). You can declare a type indexed by strings (since all property names are ultimately strings or Symbols, in this case strings) where the property values are numbers or strings:

    declare type SomeObjType = {
        [key: string]: number | string
    };
    

    and then getPropertyis:

    function getProperty<T extends SomeObjType>(obj: T, key: string): number | string {
      return obj[key];
    }
    

    and you can use it like this (in this case, I use JSON.parse to simulate receiving this data from outside the scope of the program):

    const someObj: SomeObjType = JSON.parse(`{
      "myValue": 123,
      "otherProperty": "321"
    }`);
    
    console.log(getProperty(someObj, 'myValue'));
    console.log(getProperty(someObj, 'otherProperty'));
    

    On the playground

    But that doesn't buy you much, and closes off the possibility that the property values are something other than numbers or strings.

    You may need to just use object:

    function getProperty(obj: object, key: string) {
      return obj[key];
    }
    
    const someObj = JSON.parse(`{
      "myValue": 123,
      "otherProperty": "321"
    }`);
    
    console.log(getProperty(someObj, 'myValue'));
    console.log(getProperty(someObj, 'otherProperty'));
    

    On the playground