question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Feature: Composing spread operator on Union types and static_key function to enable generic capability

See original GitHub issue

Related issues: #13542 #15759 (slightly different take and example on closed issues)

I originally posted on issue #15759 but decided that they were different enough to be looked at separately and that issue doesn’t address a generic capability. Look back at https://github.com/Microsoft/TypeScript/issues/15759#issuecomment-314157616 for clunky workaround examples.

Proposal

  1. Add spread operator ... to string literal union types acting as an string array or generator. Type T applying spread operator will result in type T[]. For example:
type Group = 'red' | 'blue' | 'green';
const groups = ...Group; // groups has type of Group[] with values ['red', 'green', 'blue']
  1. Add static function static_key or keyof that transforms a static interface to a string literal union of all combined types. For example:
interface Groups<T, U, V> {
  red: T;
  green: U;
  blue: V;
}
type groups = static_key(Groups); // 'red' | 'blue' | 'green'
  1. Composing both can result in powerful generic operations. I feel that this could bring a unique capability that string enums could not and may have been overlooked. It would be very powerful for a Record type or interface where all fields are the same type. Here is an example:
interface GroupDescription {
    name: string;
    description: string;
}

type Group = 'red' | 'blue' | 'green';
type Groups = { [K in Group]: GroupDescription };

function initGroups(groups: Groups) {
    for(const group of ...static_key(Groups)) { // group would be type Group or 'red' | 'blue' | 'green'
        processGroup(name); //Generically Process property
    }
}

function processGroup(group: Group) { }

I noticed I don’t need ...static_key but instead directly use the spread operator on the union type ...Group. The point is that they compose well together and they add generic capability.

Issue Analytics

  • State:open
  • Created 6 years ago
  • Reactions:38
  • Comments:9 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
mblazecommented, Dec 7, 2020

Converting literal types to values is something that other languages already do and it would be nice to have that in TS as well. IMO a valueof keyword would be a good choice for that as it could have a more general usecase.

Example:

const a: 'a' = valueof 'a'
console.log(a) // 'a'
const s: ['a', 'b', 'c'] = valueof ['a', 'b', 'c']
console.log(s) // ['a', 'b', 'c']

The only thing missing then would be a conversion from a literal union to a literal tuple but I believe that this is already possible with some advanced typing.

type UnionToTuple<U> = ???
type Sex = 'male' | 'female'
const sexes: Array<Sex> = valueof UnionToTuple<Sex>
console.log(sexes) // ['male', 'female']

I’d really love to see this feature in TypeScript as it would allow for generating arbitrary values and functions based on types (validation, reducers, etc.).

1reaction
renaudbardetcommented, Jan 4, 2019

@rubenpieters the difference is that keyof is in type space, meaning once the code is compiled there is no trace of the literals expressed in the union type, whereas the proposed static_keys (or …T spread operator) is in runtime space, so those literals would be converted to an actual array that you can parse and check runtime values against

I have been searching this issue for the same reasons as @matheo who already explained brilliantly my concerns, current solution (synced array) and its drawbacks, here are additional considerations.

I am using a type definition made by someone else and need parameter validation against a literal union. Asking this 3rd party to change their library so that those union types are expressed as enums would solve this as enums generate a double mapped dictionary at runtime.

type T = "value1" | "value2" | "value3"
// is type equivalent to
enum E {
  value1,
  value2,
  value3
}
var paramToCheck = 'value1'
E.hasOwnProperty( paramToCheck ) // true

but

  • some parameter values do not convert nicely to enum members
type T = "strange value" | "valeur étrange"
// is type equivalent to
enum E {
  "strange value",
  "valeur étrange"
}
// this works but feels odd and adds confusion with the E[number] reverse mapping convention
const param:E = E["valeur étrange"]
  • other users of this library will get runtime values added to their codebase while they may just want the type definition, I think affecting runtime space should be my choice

Edit: added for completeness Another solution would be to use the switch exhaustiveness pattern to check for the union type values, like the manually synced array solution but type safe:

type T = "value1" | "value2" | "value3"
function ensureT(s:string): T {
  let t: T = s as T
  switch(t) {
    case 'value1':
    case 'value2':
    case 'value3':
      return t
    default: (function(x:never):never{
                    throw new Error(`unexpected value for T: ${s}`)
                  }(t))
   }
}

By assuming our parameter is a valid T and passing it through a (never)=>never in the default case, the type checker will complain if the switch is not exhaustive, which ensures synchronisation with the type definition. At runtime, t = s as T will actually let pass any wrong value, which will be caught in the default case of the switch and trigger the error throw.

Read more comments on GitHub >

github_iconTop Results From Across the Web

The object spread operator can't be used with union types
I believe this is actually correct behaviour. The solution here is to give the correct type annotation to your function: const f3: Y...
Read more >
typescript union types from spread operator - Stack Overflow
I get the error: Argument of type 'string' is not assignable to parameter of type 'number'. How can I make it so that...
Read more >
Spread syntax (...) - JavaScript - MDN Web Docs - Mozilla
The spread (...) syntax allows an iterable, such as an array or string, to be expanded in places where zero or more arguments...
Read more >
A Framework for Designing Cryptographic Key Management ...
Section 6 Cryptographic Keys and Metadata covers the most critical elements of a. CKMS: keys and metadata, by enumerating and defining possible key...
Read more >
ArubaOS 8.9.0.0 User Guide - Aruba Networks
Working with Role Assignment with Machine Authentication Enabled ... same capacity and features of the failed stand-alone controller, it requires the same ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found