Skip to content Skip to sidebar Skip to footer

Typescript: Type To Contain Any Value Except Values From A Predefined Set

Is it possible to have a type that contains any value BUT the values from a predefined set? type Fruit = 'Apple' | 'Banana' | 'Orange' type NotFruit = ??? const a: NotFruit = 'Car

Solution 1:

Negated types are not currently supported in TypeScript as concrete types, but maybe they will be someday.

For now, the only way to express negated types is indirectly, via a generic type that verifies whether a condition is met.

Something like:

type DefinitelyNot<T, C> = [T] extends [C]
  ?Invalid<[C, "is prohibited because it might be", T]>
  : [C] extends [T]
    ?Invalid<[C, "is prohibited because it is assignable to", T]>
    : C;

The type DefinitelyNot<T, C> takes a type T to negate, and a candidate type C. If we can be sure that C is not compatible with T, then we return C itself. Otherwise, we return something that C will not match, specifically an Invalid type that causes an error. Well, we would do that if invalid types were supported, which they're currently not. So we need a workaround there too:

typeInvalid<Msg> = Msg & Error;

It makes for some fairly ugly error messages, but at least the developer might have some chance of figuring out why the error appeared. Then we can make a function which takes a type T and produces a new function that only accepts arguments not compatible with T:

const makeNot = <T>() => <C>(val: C & DefinitelyNot<T, C>): C => val;

Let's try it out:

typeFruit = "Apple" | "Banana" | "Orange";

const asNotFruit = makeNot<Fruit>();

const a = asNotFruit("Carrot"); // okayconst b = asNotFruit("Apple"); // error//  ┌--------------> ~~~~~~~ // ["Apple", "is prohibited because it is assignable to", Fruit]functionsomeRandomFunction(x: string, y: number) {
  const c = asNotFruit(x); // error// ┌---------------> ~// [string, "is prohibited because it might be", Fruit]const d = asNotFruit(y); // okay (a number cannot be Fruit)
}

As you can see, "Carrot" and number were accepted because those are definitely not Fruit. "Apple" was rejected because it definitely isFruit, and string was rejected because it might beFruit.

Not sure if you want to use this sort of solution, but I figured I'd include it anyway. Hope that helps. Good luck!

Link to code

Solution 2:

I would answer that this is definitely not possible with Typescript. There is no negation operator for sets in the language.

You can create an instanceOf typeguard though.

https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards

type Fruit = "Apple" | "Orange";

function isFruit(fruit: string): fruit is Fruit {
    return !(['Apple', 'Orange'].includes(fruit));
}

Post a Comment for "Typescript: Type To Contain Any Value Except Values From A Predefined Set"