5

I want to implement a switch-like function similar to the when operator in Kotlin in Typescript.

Example usage:

const a = 30;
const b = 10;

const result = when(
   {[a < b]: 'somethin'},
   {[a > b]: 'somethin else'},
   {[a >= b]: 'somethin else 2'},
)

>> result == 'something else'

It returns the value of the first case with condition evaluated to true.

I tried to do something like this:

type Case<T> = { [key: boolean]: T };

function when<T>(...cases: Case<T>[]) {
  const index = cases.findIndex(c => c.hasOwnProperty(true));
  return index >= 0 ? cases[index][true] : undefined;
}

but the TS compiler is complaining with An index signature parameter type must be either 'string' or 'number'.ts(1023).

Also, when I try to do something like this:

const foo = {[a > b]: 'something'};

TS is again erroring out with A computed property name must be of type 'string', 'number', 'symbol', or 'any'.ts(2464)

This can easily be done in pure JS since the boolean result in the computed property is automatically coerced (converted to) string.

I could not find any way of doing this online so a settled with doing this instead:

function when<T>(...cases: [boolean, T][]) {
  const index = cases.findIndex(([condition, _]) => condition);
  return index >= 0 ? cases[index][1] : undefined;
}

when(
   [a < b, 'somethin'],
   [a > b, 'somethin else'],
   [a >= b, 'somethin else 2'],
)

This is fine, but I find the syntax of the first example more pleasing to look at.

Am I missing something or is this limitation of the current spec?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Iliya Zhechev
  • 101
  • 2
  • 5

2 Answers2

0

TypeScript has strong typing, you should not expect such coercion. Object keys in JavaScript must be string or number. That is why TypeScript is asking you to not use boolean values. You could try the following solution:

type Case<T> = { [key in "true" | "false"]: T };

However, you need to be aware that you cannot have two identical keys: see the example below:

let sampleObject = {
    "true": 'something',
    "false": 'something else',
    "false": 'something else 2'
}

console.log(sampleObject);
// will print { true: "something", false: "something else 2" }
// note that the second value is not available because
// you cannot have 2 "false" keys
```

saulotoledo
  • 1,737
  • 3
  • 19
  • 36
  • 4
    The type you suggest would work for your example, but it would not let me have boolean computed property names, which is what I want at the end of the day. Since TS is `superset` of JS I expect to be able to do something like this `const a = {[true]: 'something'}`, which is valid JS, in TS. – Iliya Zhechev Mar 19 '20 at 08:51
  • @IliyaZhechev "*TS is superset of JS*" does not mean what you think it does, see [What does "all legal JavaScript is legal TypeScript" mean?](https://stackoverflow.com/questions/41750390/what-does-all-legal-javascript-is-legal-typescript-mean) – Bergi Aug 18 '23 at 23:04
0

It has to be string:

 {[(a < b).toString()]: 'somethin'},
  • I know it can be done like that. But this makes the interface pretty ugly. At least in my eyes. Plus the point of the question is not exactly addressed here. – Iliya Zhechev Jul 20 '21 at 20:07