TypeScript second parameter type based on first parameter type

I have an enum of possible values for the first parameter and I want the second parameter to be based on the first parameter. So, if NAME is given I want the second parameter to be as string. If AGE is given, I want the second parameter to be a number.

How can I do something like this?

enum KeyType {
   NAME,
   AGE
}

class MyClass {
   public static setProperty(key: KeyType.NAME, value: string): void { }
   public static setProperty(key: KeyType.AGE, value: number): void { } 
}

And I would want to call the method like this:
MyClass.setProperty(KeyType.NAME, 'John');

Also, this should show an error:
MyClass.setProperty(KeyType.NAME, 5); // 5 is not a string

In this example it doesn't work as the key type is wrongly defined (key type is actually the value of the enum, so key type is 0).

I am also open to suggestions about a different approach to having this functionality of only allowing a specific type for a specific parameter key.

Answers:

Answer

You want to use function overloads to get the type checking working properly:

enum KeyType {
   NAME,
   AGE
}

class MyClass {
   public static setProperty(key: KeyType.NAME, value: string): void;
   public static setProperty(key: KeyType.AGE, value: number): void;
   public static setProperty(key: KeyType, value: (string | number)): void {}
}

or simpler, just use strings:

class MyClass {
   public static setProperty(key: 'name', value: string): void;
   public static setProperty(key: 'age', value: number): void;
   public static setProperty(key: string, value: (string | number)): void {}
}

MyClass.setProperty('name', 42); // Argument of type '"name"' is not assignable to parameter of type '"age"'.
MyClass.setProperty('age', 42); // Ok
MyClass.setProperty('name', 'foo'); // Ok
MyClass.setProperty('age', 'foo'); // Argument of type '"foo"' is not assignable to parameter of type 'number'.

And of course you don't have to list the literal strings in the overloads, you can group similar ones into a type definition:

type StringProperty = 'name' | 'address';
type NumberProperty = 'age' | 'salary';
class MyClass {
   public static setProperty(key: StringProperty, value: string): void;
   public static setProperty(key: NumberProperty, value: number): void;
   public static setProperty(key: string, value: (string | number)): void {}
}
Answer

You could try this

MyClass.setProperty(key: KeyType, value: string |number) {
  // check key type and throw error as appropriate 
}
Answer

I'm using another approach, using an object.

For example: let's say that we are creating a function to send analytics data, so we have an event name and a payload about this event...

enum TEvents {
  productView = 'web/product/view',
  productPutInCart = 'web/product/put-in-cart'
}

type TLogEvent = ({ event, data }:
  {
    event: TEvents.productView,
    payload: {
      productId: number
    }
  } |
  {
    event: TEvents.productPutInCart,
    payload: {
      productId: number,
      amount: number
    }
  }
) => void

const logEvent: TLogEvent = ({ event, data }) => {
  ...
Answer

javascript only contain object, so if you want to use enum, just define it like and object then call it like:

var enum1 = { SMALL:"asjdh", MEDIUM:2};
console.log(enum1.SMALL)
console.log(enum1.MEDIUM)
Answer

What about this approach - if your class has two properties like age and name, your setProperty function can get string or age and based on type assign to specific property, example:

class MyClass {
    public age:number;
    public name:string;

    setProperty(value: string|number) {
        if (typeof value === 'string') {
            this.name = value;
        } else if (typeof value === 'number') {
            this.age = value;
        }
    }
}

let myClass = new MyClass();

myClass.setProperty("John");
console.log(myClass.name);
myClass.setProperty(60);
console.log(myClass.age);

Of course using this trick with value with two type (string or number) you can done it as you wanted:

setProperty(key: string, value: string|number) {
    if (key === 'AGE') {
        this.age = value;
    } else if (key === 'NAME') {
        this.name = value;
    }
}

But in this case you need to write more code to protect this part.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.