I don't think this is what the type system is for. By enforcing this as a compile-time rule (which is all Typescript's typings ever are), you're ensuring that Typescript will only allow values that it can completely determine at compile time. This would disallow in-place modification of an array, even one that would otherwise fit your rules. The usability cost of this interface would likely exceed the error-catching value it would give your API's consumers.
let array: Units = ['RemainingUnits'];
if (condition) {
array.push('ActualUnits'); // not allowed; Typescript can't reason about it
}
return array;
Besides, Javascript has some effective ways to enforce non-duplicate behavior (sets or object keys) that would be very natural to what you want, and would also allow runtime behaviors that users would want (like being able to modify the list before passing it in).
type Units = {
RemainingUnits: boolean,
ActualUnits: boolean,
PlannedUnits: boolean
}
If you really want this, you'll need to spell it out:
type A = 'RemainingUnits';
type B = 'ActualUnits';
type C = 'PlannedUnits';
type Units = [A] | [B] | [C]
| [A, B] | [A, C] | [B, A] | [B, C] | [C, A] | [C, B]
| [A, B, C] | [A, C, B] | [B, A, C] | [B, C, A] | [C, A, B] | [C, B, A];
interface IEntity {
values: Units;
}
const example1: IEntity = { values: ['RemainingUnits', 'PlannedUnits'] }
const example2: IEntity = { values: ['RemainingUnits', 'RemainingUnits'] } //error
const example3: IEntity = { values: [] } //error
typescript playground