In TypeScript, there is no definitive method to prevent specific string literals from being used as known property keys of an object type. While there is no official feature that directly addresses this issue, the approach you are taking is considered the conventional method.
One workaround is to utilize an optional property that allows for a property to be absent, with a type of the impossible `never` type that cannot typically have a value. The recommended way to achieve `{value?: never}` is by creating an object without a `value` property. To ensure this is the sole requirement, you can include an index signature `{[k: string]: any}`. It is essential to use the `any` type instead of `unknown` as `any` permits interface types without explicit index signatures, while `unknown` does not.
type NoValue<T extends { value?: never, [key: string]: any }> = T
or
function noValue<T extends { value?: never, [key: string]: any }>(t: T) { }
noValue({}); // valid
noValue({ a: 123 }); // valid
noValue({ a: 123, value: 456 }); // error!
Despite this approach, there are some edge cases to consider. One common scenario is
noValue({ value: undefined }); // usually valid
where `undefined` is considered an acceptable property type for optional properties. Enabling the `--exactOptionalPropertyTypes` compiler option can prevent this behavior, but it adds strictness not included in the standard `--strict` suite of options. Many users find `--exactOptionalPropertyTypes` cumbersome to use.
A less frequent edge case is the possibility of obtaining a value of type `never` when the compiler is confident that an exception will be thrown before reaching that point in runtime. For instance:
const v = {
get value(): never {
throw new Error("I DON'T LIKE YOU")
}
};
noValue(v); // valid
In this scenario, the compiler recognizes `v` as being of type `{value: never}` because an exception will occur if `v.value` is evaluated. This situation is rare but technically feasible.
Ultimately, there are limitations to preventing the compiler from losing track of whether a value has a `value` property. The compiler can always widen a type to a supertype and treat optional properties as assignable to a type without a known property. As a result, the best approach in TypeScript is to discourage rather than prohibit an object property.
Link to Playground with code