Type for "every possible string value except ..."
There isn't a general solution to this problem since there is no way to express in the typescript type system the fact that a string can be any value except a list. (One might think the conditional type Exclude<string, ReservedNames>
would work but it does not, it just evaluates back to string).
As a work around, if we have a function and we specifically want to not allow certain constants to be passed in we can us a conditional type to check for ReservedNames
and, if the passed in parameter is ReservedNames
then type the input parameter in such a way it is effectively impossible to satisfy (using an intersection type).
type ReservedNames = "this" | "that"
type FooName = Exclude<string, ReservedNames>;
const f1 : FooName = "This" // Works
const f2 : FooName = "this" // One might expect this to work but IT DOES NOT as FooName is just evaluates to string
function withName<T extends string>(v: T & (T extends ReservedNames ? "Value is reserved!": {})) {
return v;
}
withName("this"); // Type '"this"' is not assignable to type '"Value is reserved!"'.
withName("This") // ok
Playground
This isn't currently possibly in Typescript, however you can create a generic type that can handle many of the practical use cases if you add the concrete string value as a parameter of FooName
.
type ReservedNames = "this" | "that"
type NotA<T> = T extends ReservedNames ? never : T
type NotB<T> = ReservedNames extends T ? never : T
type FooName<T> = NotA<T> & NotB<T>
const f1: FooName<'This'> = 'This' // works
const f2: FooName<'this'> = 'this' // error
const f3: FooName<string> = 'this' //error
const f4: FooName<any> = 'this' // error
const f5: FooName<unknown> = 'this' // error
And in a function it works as expected if you make the function generic on the string value:
function foo<T extends string> (v: FooName<T>) {
...
}
foo('this') // error
foo('This') // works