PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Monday, November 7, 2022

[FIXED] How to force a Set object to contain all values within an enum - TS

 November 07, 2022     enums, set, typescript     No comments   

Issue

I have the following enum:
enum GREETINGS { HELLO = 'Hello', HI = 'Hi' }

I want to create a type for a Set data structure which would force the user to include all of the enum values within it, like below:
type MyGreetings = Set<GREETINGS>

The expected outcome should cause the following line to be highlighted with a warning stating that not all required values are included within the result:
const result: MyGreetings = new Set([GREETINGS.HELLO]);


Solution

One problem is that TypeScript considers Set<GREETINGS.HELLO> to be assignable to / compatible with Set<GREETINGS>; in theory that should mean that if you want Set<GREETINGS> to be assignable to MyGreetings, then by transitivity of assignability, Set<GREETINGS.HELLO> will also be assignable to MyGreetings, and you're stopped before you've begun. But:

The fact that Set<GREETINGS.HELLO> is assignable to Set<GREETINGS> is actually using an intentional hole in the type system: method parameter bivariance. Method parameter bivariance is very useful, but it is not type safe:

const justHello = new Set<GREETINGS.HELLO>([GREETINGS.HELLO]);
const stillJustHello: Set<GREETINGS> = justHello;
stillJustHello.add(GREETINGS.HI) // <-- no error, oops

By "widening" justHello to stillJustHello, the compiler has forgotten that it should not accept any member but GREETINGS.HELLO.

So one way to get closer to what you want is to use the --strictFunctionType compiler option (which is part of the --strict suite of compiler options and is recommended in general) and rewrite at least one Set method signature using a function property type instead of a method signature. Let's look at add() and see the difference:

type Method = { add(x: GREETINGS): void } // method syntax
const m: Method = new Set<GREETINGS.HELLO>(); // okay

type FuncProp = { add: (x: GREETINGS) => void } // function syntax
const f: FuncProp = new Set<GREETINGS.HELLO>(); // error

So let's try something like:

type MyGreetings = Set<GREETINGS> & { add: (g: GREETINGS) => MyGreetings };

If we use that, you get the desired result:

const result: MyGreetings = new Set([GREETINGS.HELLO]); // error!
//  Type 'GREETINGS' is not assignable to type 'GREETINGS.HELLO'

const okay: MyGreetings = new Set([GREETINGS.HELLO, GREETINGS.HI]); // okay

Hooray!


Well, kind of. The compiler really cannot know exactly which values have been passed into a Set. It can infer that new Set(...) is of type Set<X> for some X, but there are ways to manually specify a wider X or get the compiler to infer a wider X. And then all the progress from above is lost.

For example, since a Set<GREETINGS> is allowed to contain just a subset of GREETINGS, someone can do the following manual specification:

const oops: MyGreetings = new Set<GREETINGS>([GREETINGS.HELLO]) // no error

Or someone could get the compiler to infer a union this way:

const alsoOops: MyGreetings = new Set([
  Math.random() < 0.5 ? GREETINGS.HELLO : GREETINGS.HI
]); // no error

Neither of those can be an error; the value on the right hand side of the equals sign is a Set<GREETINGS>. And you need Set<GREETINGS> to be assignable to MyGreetings, so the assignments are also not errors. There's not much that can be done about this.

If in practice you don't think someone will create a set in these or other pathological ways, then maybe you can use MyGreetings. Otherwise, if you really need to guarantee something, you should consider changing your design in some way. But this is probably out of scope for the question, so I'll stop there.


Playground link to code (tested and working for typescript version 3.5.1 and higher)



Answered By - jcalz
Answer Checked By - Gilberto Lyons (PHPFixing Admin)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing